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 }