1 /*
2  * Copyright (c) 2017 SEL
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  * See the GNU Lesser General Public License for more details.
13  * 
14  */
15 module sel.hncom.util;
16 
17 import std.conv : to;
18 import std.zlib : Compress, UnCompress, HeaderFormat;
19 
20 import sel.hncom.about;
21 import sel.hncom.io : IO;
22 
23 @clientbound @serverbound struct Uncompressed {
24 
25 	enum ubyte ID = 5;
26 
27 	/**
28 	 * If not 0 the same packet in the next field has the same id.
29 	 * Otherwise the packet id is the first byte of the packet.
30 	 */
31 	ubyte id;
32 	ubyte[][] packets;
33 
34 	mixin IO!(id, packets);
35 
36 	/**
37 	 * Adds a serialised packet to the array of packets.
38 	 */
39 	typeof(this) add(ubyte[] packet) {
40 		if(this.id == 0) {
41 			this.packets ~= packet;
42 		} else {
43 			assert(packet[0] == this.id);
44 			this.packets ~= packet[1..$];
45 		}
46 		return this;
47 	}
48 
49 }
50 
51 @clientbound @serverbound struct Compressed {
52 
53 	enum ubyte ID = 6;
54 
55 	/**
56 	 * Length of the uncompressed buffer.
57 	 */
58 	uint length;
59 
60 	/**
61 	 * Same as Uncompressed's id field.
62 	 */
63 	ubyte id;
64 
65 	/**
66 	 * Compressed data.
67 	 */
68 	ubyte[] payload;
69 
70 	mixin IO!(length, id, payload);
71 
72 	/**
73 	 * Creates a Compressed from an Uncompressed packet.
74 	 * The Uncompressed packet is encoded and the data is compressed using
75 	 * zlib's deflate algorithm.
76 	 */
77 	static Compressed compress(Uncompressed uncompressed, int level=6) {
78 		ubyte[] buffer = uncompressed.encode()[1..$];
79 		auto ret = Compressed(buffer.length.to!uint, uncompressed.id);
80 		Compress compress = new Compress(level, HeaderFormat.deflate);
81 		ret.payload = cast(ubyte[])compress.compress(buffer);
82 		ret.payload ~= cast(ubyte[])compress.flush();
83 		return ret;
84 	}
85 
86 	static Compressed compress(ubyte[][] packets) {
87 		assert(packets.length);
88 		ubyte id = packets[0][0];
89 		foreach(packet ; packets[1..$]) {
90 			if(packet[0] != id) {
91 				id = 0;
92 				break;
93 			}
94 		}
95 		if(id != 0) {
96 			// remove ids
97 			foreach(ref packet ; packets) {
98 				packet = packet[1..$];
99 			}
100 		}
101 		return compress(Uncompressed(id, packets));
102 	}
103 
104 	Uncompressed uncompress() {
105 		UnCompress uncompress = new UnCompress(this.length);
106 		ubyte[] buffer = cast(ubyte[])uncompress.uncompress(this.payload);
107 		buffer ~= cast(ubyte[])uncompress.flush();
108 		return Uncompressed.fromBuffer(this.id ~ buffer);
109 	}
110 
111 }
112 
113 unittest {
114 
115 
116 
117 }