1 module cubf.reader; 2 import std.typecons : Tuple, tuple; 3 4 struct CUBFReader { 5 private { 6 import std.system : Endian, endian; 7 import std.bitmanip : bigEndianToNative, littleEndianToNative; 8 9 ubyte[] source; 10 bool requireChecksum, hadAnyChunksSoFar; 11 Type current; 12 Endian endianess; 13 ubyte whereHeader; 14 ulong headerOffset; 15 } 16 17 this(ubyte[] source, bool requireChecksum=false) { 18 this.requireChecksum = requireChecksum; 19 20 this.whereHeader = source[4]; 21 if (source[5] == 'B') 22 this.endianess = Endian.bigEndian; 23 else 24 this.endianess = Endian.littleEndian; 25 26 if (this.whereHeader == 'O') { 27 if (endianess == Endian.bigEndian) { 28 headerOffset = bigEndianToNative!ulong(source[8 .. 16])-16; 29 } else { 30 headerOffset = littleEndianToNative!ulong(source[8 .. 16])-16; 31 } 32 33 this.source = source[16 .. $]; 34 } else { 35 this.source = source[8 .. $]; 36 } 37 38 if (source.length > 0) 39 popFront; 40 } 41 42 @property { 43 Type front() { 44 return current; 45 } 46 47 bool empty() { 48 return source.length == 0 && current is Type.init; 49 } 50 } 51 52 CUBFReader save() { 53 // implicit copy ;) 54 return this; 55 } 56 57 void popFront() { 58 if (source.length == 0) { 59 current = Type.init; 60 return; 61 } 62 63 uint length, hash; 64 bool isHeader; 65 66 if (endianess == Endian.bigEndian) { 67 length = bigEndianToNative!uint(source[$-4 .. $][0 .. 4]); 68 hash = bigEndianToNative!uint(source[$-8 .. $-4][0 .. 4]); 69 } else { 70 length = littleEndianToNative!uint(source[$-4 .. $][0 .. 4]); 71 hash = littleEndianToNative!uint(source[$-8 .. $-4][0 .. 4]); 72 } 73 74 if (this.whereHeader == 'O') { 75 isHeader = headerOffset == source.length-1; 76 } else if (this.whereHeader == 'S') { 77 isHeader = !this.hadAnyChunksSoFar; 78 } else if (this.whereHeader == 'E') { 79 isHeader = this.source.length <= length + 12; 80 } 81 this.hadAnyChunksSoFar = true; 82 83 ubyte[] data; 84 if (this.source.length > length + 12) { 85 data = source[$-(length + 12) .. $-12]; 86 current = Type(cast(char[4])source[$-12 .. $-8][0 .. 4], data, isHeader); 87 this.source = source[0 .. $-(length + 12)]; 88 } else { 89 data = source[0 .. $-12]; 90 current = Type(cast(char[4])source[$-12 .. $-8][0 .. 4], data, isHeader); 91 this.source = null; 92 } 93 94 if (requireChecksum) { 95 import std.digest.crc; 96 97 if (*cast(uint*)crc32Of(data).ptr != hash) { 98 assert(0, "hash not equals for data"); 99 } 100 } 101 } 102 103 alias Type = Tuple!(char[4], "name", ubyte[], "data", bool, "isHeader"); 104 }