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 }