1 module voxd.utils; 2 3 import std.file, 4 std.range, 5 std.traits, 6 std.string, 7 std.format; 8 9 struct VoxColor 10 { 11 ubyte r, g, b, a; 12 13 this(ubyte r_, ubyte g_, ubyte b, ubyte a_) 14 { 15 r = r_; 16 r = g; 17 r = b; 18 r = a; 19 } 20 21 this(uint value) 22 { 23 a = (value >> 24); 24 b = (value >> 16) & 255; 25 g = (value >> 8) & 255; 26 r = value & 255; 27 } 28 } 29 30 31 /// The simple structure currently used in vox-d. Expect changes about this. 32 struct VOX 33 { 34 public: 35 int width; 36 int height; 37 int depth; 38 39 VoxColor[] voxels; 40 41 ref inout(VoxColor) voxel(int x, int y, int z) inout 42 { 43 return voxels[x + y * width + z * width * height]; 44 } 45 46 int numVoxels() pure const nothrow 47 { 48 return width * height * depth; 49 } 50 } 51 52 /// The one type of Exception thrown in this library 53 final class VoxdException : Exception 54 { 55 @safe pure nothrow this(string message, string file =__FILE__, size_t line = __LINE__, Throwable next = null) 56 { 57 super(message, file, line, next); 58 } 59 } 60 61 62 private template IntegerLargerThan(int numBytes) if (numBytes >= 1 && numBytes <= 8) 63 { 64 static if (numBytes == 1) 65 alias IntegerLargerThan = ubyte; 66 else static if (numBytes == 2) 67 alias IntegerLargerThan = ushort; 68 else static if (numBytes <= 4) 69 alias IntegerLargerThan = uint; 70 else 71 alias IntegerLargerThan = ulong; 72 } 73 74 ubyte popUbyte(R)(ref R input) if (isInputRange!R) 75 { 76 if (input.empty) 77 throw new VoxdException("Expected a byte, but end-of-input found."); 78 79 ubyte b = input.front; 80 input.popFront(); 81 return b; 82 } 83 84 void skipBytes(R)(ref R input, int numBytes) if (isInputRange!R) 85 { 86 for (int i = 0; i < numBytes; ++i) 87 popUbyte(input); 88 } 89 90 // Generic integer parsing 91 auto popInteger(R, int NumBytes, bool WantSigned, bool LittleEndian)(ref R input) if (isInputRange!R) 92 { 93 alias T = IntegerLargerThan!NumBytes; 94 95 T result = 0; 96 97 static if (LittleEndian) 98 { 99 for (int i = 0; i < NumBytes; ++i) 100 result |= ( cast(T)(popUbyte(input)) << (8 * i) ); 101 } 102 else 103 { 104 for (int i = 0; i < NumBytes; ++i) 105 result = (result << 8) | popUbyte(input); 106 } 107 108 static if (WantSigned) 109 return cast(Signed!T)result; 110 else 111 return result; 112 } 113 114 // Generic integer writing 115 void writeInteger(R, int NumBytes, bool LittleEndian)(ref R output, IntegerLargerThan!NumBytes n) if (isOutputRange!(R, ubyte)) 116 { 117 alias T = IntegerLargerThan!NumBytes; 118 119 auto u = cast(Unsigned!T)n; 120 121 static if (LittleEndian) 122 { 123 for (int i = 0; i < NumBytes; ++i) 124 { 125 ubyte b = (u >> (i * 8)) & 255; 126 output.put(b); 127 } 128 } 129 else 130 { 131 for (int i = 0; i < NumBytes; ++i) 132 { 133 ubyte b = (u >> ( (NumBytes - 1 - i) * 8) ) & 255; 134 output.put(b); 135 } 136 } 137 } 138 139 // Reads a big endian integer from input. 140 T popBE(T, R)(ref R input) if (isInputRange!R) 141 { 142 return popInteger!(R, T.sizeof, isSigned!T, false)(input); 143 } 144 145 // Reads a little endian integer from input. 146 T popLE(T, R)(ref R input) if (isInputRange!R) 147 { 148 return popInteger!(R, T.sizeof, isSigned!T, true)(input); 149 } 150 151 // Writes a big endian integer to output. 152 void writeBE(T, R)(ref R output, T n) if (isOutputRange!(R, ubyte)) 153 { 154 writeInteger!(R, T.sizeof, false)(output, n); 155 } 156 157 // Writes a little endian integer to output. 158 void writeLE(T, R)(ref R output, T n) if (isOutputRange!(R, ubyte)) 159 { 160 writeInteger!(R, T.sizeof, true)(output, n); 161 } 162 163 // Reads RIFF chunk header. 164 void getRIFFChunkHeader(R)(ref R input, out uint chunkId, out uint chunkSize) if (isInputRange!R) 165 { 166 chunkId = popBE!uint(input); 167 chunkSize = popLE!uint(input); 168 } 169 170 // Writes RIFF chunk header (you have to count size manually for now...). 171 void writeRIFFChunkHeader(R)(ref R output, uint chunkId, uint chunkSize) if (isOutputRange!(R, ubyte)) 172 { 173 writeBE!uint(output, chunkId); 174 writeLE!uint(output, chunkSize); 175 } 176 177 template RIFFChunkId(string id) 178 { 179 static assert(id.length == 4); 180 uint RIFFChunkId = (cast(ubyte)(id[0]) << 24) 181 | (cast(ubyte)(id[1]) << 16) 182 | (cast(ubyte)(id[2]) << 8) 183 | (cast(ubyte)(id[3])); 184 }