1 | // tChunk.cpp  |
2 | //  |
3 | // The generic chunk based file format for all Tacent projects. This interface can be used for creating and reading  |
4 | // arbitrary data files. At the bottom of this file are all the global chunk ids. There are 268 million available, so  |
5 | // just add chunk IDs as necessary. The format supports alignment of data from 4 bytes to 512 bytes in powers of 2.  |
6 | //  |
7 | // A chunk consists of:  |
8 | // 1) A 4 byte specifier. From most-significant bit to least:  |
9 | // 1 bit: Is-container boolean.  |
10 | // 3 bits: Alignment shift.  |
11 | // 28 bits: Chunk ID.  |
12 | // 2) A 4 byte unsigned int (big-endian? or plat-dependent?). This is the chunk data size not including any  |
13 | // leading alignment padding.  |
14 | // 3) Zero or more alignment padding bytes.  |
15 | // 4) The data.  |
16 | // 5) Any necessary ending alignment pad to achieve 4-byte alignment.  |
17 | //  |
18 | // Multiple chunks within a file are consecutive and chunks may contain sub chunks. Regardless of the data alignment  |
19 | // size, all chunks start at 4-byte aligned addresses. Data alignment requirements may be stricter.  |
20 | //  |
21 | // Technically chunks may contain both data and sub-chunks but if you do it this way your chunk reading code will need  |
22 | // to be smart enough to know where the data and sub-chunks are. By convention we do _not_ do this. A chunk with  |
23 | // sub-chunks should not contain any other straight data, only other sub-chunks.  |
24 | //  |
25 | // A chunk ID of 0x00000000 is considered invalid so don't use it. It is also the ID returned by the uber-chunk  |
26 | // (tChunkReader object).  |
27 | //  |
28 | // To maintain good file compatibility, chunks whose ID are unknown should be ignored and not treated as an error.  |
29 | //  |
30 | // If the contents of a chunk need to change you'd better use a new chunk ID. If you really like the name of the chunk  |
31 | // you can keep it though. Simply rename it (e.g. from tChunkID_NICENAME to tChunkID_OBSOLETE_NICENAME) while keeping  |
32 | // the ID the same. You can now use the chunk name (tChunkID_NICENAME) elsewhere with a new ID. The obsolete chunk, of  |
33 | // course, must contain the same stuff as the original nice name chunk.  |
34 | //  |
35 | // Copyright (c) 2006, 2017 Tristan Grimmer.  |
36 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby  |
37 | // granted, provided that the above copyright notice and this permission notice appear in all copies.  |
38 | //  |
39 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL  |
40 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,  |
41 | // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN  |
42 | // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR  |
43 | // PERFORMANCE OF THIS SOFTWARE.  |
44 |   |
45 | #include <Foundation/tMemory.h>  |
46 | #include "System/tFile.h"  |
47 | #include "System/tChunk.h"  |
48 | using namespace tStd;  |
49 | using namespace tSystem;  |
50 | using namespace tMath;  |
51 |   |
52 |   |
53 | void tChunkWriter::Open(const tString& fileName, tEndianness dstEndianness)  |
54 | {  |
55 | tAssert(!WriteBuffer);  |
56 |   |
57 | #ifdef PLATFORM_WINDOWS  |
58 | if (ChunkFile)  |
59 | throw tChunkError("File already opened." );  |
60 | #else  |
61 | tAssert(!ChunkFile);  |
62 | #endif  |
63 |   |
64 | // We need to determine, based on the write endianness and the current endianness, if swaps will be necessary.  |
65 | tEndianness srcEndianness = tGetEndianness();  |
66 | NeedsEndianSwap = (srcEndianness == dstEndianness) ? false : true;  |
67 | IsContainer = true;  |
68 |   |
69 | ChunkFile = tOpenFile(fileName, "wb" );  |
70 |   |
71 | #ifdef PLATFORM_WINDOWS  |
72 | if (!ChunkFile)  |
73 | throw tChunkError("Cannot open file [%s]." , fileName.Pod());  |
74 | #else  |
75 | tAssert(ChunkFile);  |
76 | #endif  |
77 | }  |
78 |   |
79 |   |
80 | bool tChunkWriter::OpenSafe(const tString& fileName, tEndianness dstEndianness)  |
81 | {  |
82 | tAssert(!WriteBuffer);  |
83 |   |
84 | #ifdef PLATFORM_WINDOWS  |
85 | if (ChunkFile)  |
86 | return false;  |
87 | #else  |
88 | tAssert(!ChunkFile);  |
89 | #endif  |
90 |   |
91 | // We need to determine, based on the write endianness and the current endianness, if swaps will be necessary.  |
92 | tEndianness srcEndianness = tGetEndianness();  |
93 | NeedsEndianSwap = (srcEndianness == dstEndianness) ? false : true;  |
94 | IsContainer = true;  |
95 |   |
96 | ChunkFile = tOpenFile(fileName, "wb" );  |
97 |   |
98 | #ifdef PLATFORM_WINDOWS  |
99 | if (!ChunkFile)  |
100 | return false;  |
101 | #else  |
102 | tAssert(ChunkFile);  |
103 | #endif  |
104 |   |
105 | return true;  |
106 | }  |
107 |   |
108 |   |
109 | void tChunkWriter::Close()  |
110 | {  |
111 | tAssert(!WriteBuffer);  |
112 |   |
113 | if (ChunkFile)  |
114 | {  |
115 | tCloseFile(ChunkFile);  |
116 | ChunkFile = 0;  |
117 | }  |
118 |   |
119 | #ifndef PLATFORM_WINDOWS  |
120 | tAssert(!ChunkInfos.Head());  |
121 | #endif  |
122 |   |
123 | if (ChunkInfos.Head())  |
124 | {  |
125 | // Remove elements, then throw.  |
126 | while (ChunkInfo* chunkInfo = ChunkInfos.Remove())  |
127 | delete chunkInfo;  |
128 |   |
129 | #ifdef PLATFORM_WINDOWS  |
130 | throw tChunkError("Some chunks not ended." );  |
131 | #endif  |
132 | }  |
133 | }  |
134 |   |
135 |   |
136 | void tChunkWriter::BeginChunk(uint32 id, int alignment)  |
137 | {  |
138 | // Alignment variable is overridden for container chunks.  |
139 | if (id & 0x80000000)  |
140 | alignment = 4;  |
141 |   |
142 | #ifdef PLATFORM_WINDOWS  |
143 | if (!IsContainer)  |
144 | throw tChunkError("You can only begin a chunk from a container." );  |
145 |   |
146 | if (!ChunkFile && !WriteBuffer)  |
147 | throw tChunkError("No chunk file opened and no memory buffer to write to." );  |
148 |   |
149 | if  |
150 | (  |
151 | (alignment > (1 << (int(Alignment::Largest)+2))) ||  |
152 | (alignment < (1 << (int(Alignment::Smallest)+2))) ||  |
153 | !tIsPower2(alignment)  |
154 | )  |
155 | throw tChunkError("Chunk alignment value not supported." );  |
156 | #else  |
157 | tAssert(IsContainer);  |
158 | tAssert(ChunkFile || WriteBuffer);  |
159 | tAssert  |
160 | (  |
161 | (alignment <= (1 << (int(Alignment::Largest) + 2))) &&  |
162 | (alignment >= (1 << (int(Alignment::Smallest) + 2))) &&  |
163 | tIsPower2(alignment)  |
164 | );  |
165 | #endif  |
166 |   |
167 | // Ensure first nibble of id is 0x0 (data) or 0x8 (container).  |
168 | tAssert(((id & 0xF0000000) == 0x00000000) || ((id & 0xF0000000) == 0x80000000));  |
169 | int alignShift = tLog2(alignment);  |
170 | tAssert(alignShift >= 2);  |
171 | uint32 idaa = id | ((alignShift-2) << 28);  |
172 |   |
173 | IsContainer = true;  |
174 | if ((id & 0xF0000000) == 0x00000000)  |
175 | IsContainer = false;  |
176 |   |
177 | int chunkStart = WriteBuffer ? WriteBufferPos : tSystem::tFileTell(ChunkFile);  |
178 | tAssert((chunkStart % 4) == 0);  |
179 |   |
180 | if (NeedsEndianSwap)  |
181 | tSwapEndian(idaa);  |
182 |   |
183 | if (ChunkFile)  |
184 | {  |
185 | tWriteFile(ChunkFile, &idaa, sizeof(uint32)); // Chunk ID and alignment shift.  |
186 | tWriteFile(ChunkFile, &idaa, sizeof(uint32)); // Dummy size.  |
187 | }  |
188 | else  |
189 | {  |
190 | tAssert((WriteBufferSize - WriteBufferPos) >= 8);  |
191 | tMemcpy(WriteBuffer+WriteBufferPos, &idaa, sizeof(uint32)); WriteBufferPos += 4; // Chunk ID and alignment shift.  |
192 | tMemcpy(WriteBuffer+WriteBufferPos, &idaa, sizeof(uint32)); WriteBufferPos += 4; // Dummy size  |
193 | }  |
194 |   |
195 | // We need to advance the position until we meet the alignment requirements for this data.  |
196 | int dataStart = WriteBuffer ? WriteBufferPos : tSystem::tFileTell(ChunkFile);  |
197 | int numBytesPad = (dataStart % alignment) ? (alignment - (dataStart % alignment)) : 0;  |
198 | dataStart += numBytesPad;  |
199 | if (ChunkFile)  |
200 | {  |
201 | uint32 zero = 0;  |
202 | while (numBytesPad > 3)  |
203 | {  |
204 | tWriteFile(ChunkFile, &zero, 4);  |
205 | numBytesPad -= 4;  |
206 | }  |
207 |   |
208 | while (numBytesPad)  |
209 | {  |
210 | tWriteFile(ChunkFile, &zero, 1);  |
211 | numBytesPad--;  |
212 | }  |
213 | }  |
214 | else  |
215 | {  |
216 | tAssert((WriteBufferSize - WriteBufferPos) >= numBytesPad);  |
217 | for (int i = 0; i < numBytesPad; i++)  |
218 | *(WriteBuffer + WriteBufferPos + i) = 0;  |
219 |   |
220 | WriteBufferPos += numBytesPad;  |
221 | }  |
222 |   |
223 | ChunkInfo* chunkInfo = new ChunkInfo(chunkStart, dataStart);  |
224 | ChunkInfos.Insert(chunkInfo);  |
225 | }  |
226 |   |
227 |   |
228 | void tChunkWriter::EndChunk()  |
229 | {  |
230 | ChunkInfo* topChunk = ChunkInfos.Remove();  |
231 |   |
232 | #ifdef PLATFORM_WINDOWS  |
233 | if (!ChunkFile && !WriteBuffer)  |
234 | throw tChunkError("No chunk file opened and no chunk buffer on memory." );  |
235 |   |
236 | if (!topChunk)  |
237 | {  |
238 | tCloseFile(ChunkFile);  |
239 | throw tChunkError("Chunk ended but not started." );  |
240 | }  |
241 | #else  |
242 | tAssert(ChunkFile || WriteBuffer);  |
243 | tAssert(topChunk);  |
244 | #endif  |
245 |   |
246 | // We need to write the data size at the beginning of the chunk.  |
247 | int currPos = ChunkFile ? tSystem::tFileTell(ChunkFile) : WriteBufferPos;  |
248 | tAssert(topChunk->StartData > topChunk->StartChunk);  |
249 | uint32 dataSize = currPos - topChunk->StartData;  |
250 | if (NeedsEndianSwap)  |
251 | tSwapEndian(dataSize);  |
252 |   |
253 | if (ChunkFile)  |
254 | {  |
255 | tFileSeek(ChunkFile, topChunk->StartChunk + 4, tSeekOrigin::Beginning);  |
256 | tWriteFile(ChunkFile, &dataSize, sizeof(uint32));  |
257 | tFileSeek(ChunkFile, currPos, tSeekOrigin::Beginning);  |
258 | }  |
259 | else  |
260 | {  |
261 | tMemcpy((WriteBuffer + topChunk->StartChunk + 4), &dataSize, sizeof(uint32));  |
262 | }  |
263 |   |
264 | delete topChunk;  |
265 |   |
266 | // We need to advance the position until we aligned to 4 bytes so that the next chunk starts at a mult of 4 bytes.  |
267 | int numBytesPad = (currPos % 4) ? (4 - (currPos % 4)) : 0;  |
268 | if (ChunkFile)  |
269 | {  |
270 | uint8 zero = 0;  |
271 | for (int p = 0; p < numBytesPad; p++)  |
272 | tWriteFile(ChunkFile, &zero, 1);  |
273 | }  |
274 | else  |
275 | {  |
276 | tAssert((WriteBufferSize - WriteBufferPos) >= numBytesPad);  |
277 | for (int p = 0; p < numBytesPad; p++)  |
278 | *(WriteBuffer + WriteBufferPos + p) = 0;  |
279 |   |
280 | WriteBufferPos += numBytesPad;  |
281 | }  |
282 |   |
283 | IsContainer = true;  |
284 | }  |
285 |   |
286 |   |
287 | int tChunkWriter::Write(const void* data, int sizeInBytes)  |
288 | {  |
289 | #ifdef PLATFORM_WINDOWS  |
290 | if (!ChunkFile && !WriteBuffer)  |
291 | throw tChunkError("No chunk file opened and no chunk buffer on memory." );  |
292 |   |
293 | if (IsContainer)  |
294 | throw tChunkError("Not allowed to write data into a container chunk." );  |
295 | #else  |
296 | tAssert(ChunkFile || WriteBuffer);  |
297 | tAssert(!IsContainer);  |
298 | #endif  |
299 |   |
300 | int numWritten = 0;  |
301 | if (ChunkFile)  |
302 | {  |
303 | numWritten = tWriteFile(ChunkFile, data, sizeInBytes);  |
304 |   |
305 | #ifdef PLATFORM_WINDOWS  |
306 | if (numWritten != sizeInBytes)  |
307 | {  |
308 | tCloseFile(ChunkFile);  |
309 | throw tChunkError("Could not write to chunk file." );  |
310 | }  |
311 | #else  |
312 | tAssert(numWritten == sizeInBytes);  |
313 | #endif  |
314 | }  |
315 | else  |
316 | {  |
317 | tAssert((WriteBufferSize - WriteBufferPos) >= sizeInBytes);  |
318 | tMemcpy(WriteBuffer+WriteBufferPos, data, sizeInBytes);  |
319 | WriteBufferPos += sizeInBytes;  |
320 | numWritten = sizeInBytes;  |
321 | }  |
322 |   |
323 | return numWritten;  |
324 | }  |
325 |   |
326 |   |
327 | int tChunkWriter::Write(const tVector2& v)  |
328 | {  |
329 | if (NeedsEndianSwap)  |
330 | {  |
331 | tVec2 s;  |
332 | tSet(s, v);  |
333 | for (int i = 0; i < 2; i++)  |
334 | tSwapEndian(s.E[i]);  |
335 | return Write( (void*)&s, sizeof(s) );  |
336 | }  |
337 |   |
338 | return Write( (void*)&v, sizeof(v) );  |
339 | }  |
340 |   |
341 |   |
342 | int tChunkWriter::Write(const tVector3& v)  |
343 | {  |
344 | if (NeedsEndianSwap)  |
345 | {  |
346 | tVec3 s;  |
347 | tSet(s, v);  |
348 | for (int i = 0; i < 3; i++)  |
349 | tSwapEndian(s.E[i]);  |
350 | return Write( (void*)&s, sizeof(s) );  |
351 | }  |
352 |   |
353 | return Write( (void*)&v, sizeof(v) );  |
354 | }  |
355 |   |
356 |   |
357 | int tChunkWriter::Write(const tVector4& v)  |
358 | {  |
359 | if (NeedsEndianSwap)  |
360 | {  |
361 | tVec4 s;  |
362 | tSet(s, v);  |
363 | for (int i = 0; i < 4; i++)  |
364 | tSwapEndian(s.E[i]);  |
365 | return Write( (void*)&s, sizeof(s) );  |
366 | }  |
367 |   |
368 | return Write( (void*)&v, sizeof(v) );  |
369 | }  |
370 |   |
371 |   |
372 | int tChunkWriter::Write(const tQuaternion& q)  |
373 | {  |
374 | if (NeedsEndianSwap)  |
375 | {  |
376 | tQuat s;  |
377 | tSet(s, q);  |
378 | for (int i = 0; i < 4; i++)  |
379 | tSwapEndian(s.E[i]);  |
380 | return Write( (void*)&s, sizeof(s) );  |
381 | }  |
382 |   |
383 | return Write( (void*)&q, sizeof(q) );  |
384 | }  |
385 |   |
386 |   |
387 | int tChunkWriter::Write(const tMatrix2& m)  |
388 | {  |
389 | if (NeedsEndianSwap)  |
390 | {  |
391 | tMat2 s;  |
392 | tSet(s, m);  |
393 | for (int i = 0; i < 4; i++)  |
394 | tSwapEndian(s.E[i]);  |
395 | return Write( (void*)&s, sizeof(s) );  |
396 | }  |
397 |   |
398 | return Write( (void*)&m, sizeof(m) );  |
399 | }  |
400 |   |
401 |   |
402 | int tChunkWriter::Write(const tMatrix4& m)  |
403 | {  |
404 | if (NeedsEndianSwap)  |
405 | {  |
406 | tMat4 s;  |
407 | tSet(s, m);  |
408 | for (int i = 0; i < 16; i++)  |
409 | tSwapEndian(s.E[i]);  |
410 | return Write( (void*)&s, sizeof(s) );  |
411 | }  |
412 |   |
413 | return Write( (void*)&m, sizeof(m) );  |
414 | }  |
415 |   |
416 |   |
417 | int tChunkWriter::Write(const tEdge& e)  |
418 | {  |
419 | if (NeedsEndianSwap)  |
420 | {  |
421 | tEdge s(e);  |
422 | for (int i = 0; i < 2; i++)  |
423 | tSwapEndian(s.Index[i]);  |
424 | return Write( (void*)&s, sizeof(s) );  |
425 | }  |
426 |   |
427 | return Write( (void*)&e, sizeof(e) );  |
428 | }  |
429 |   |
430 |   |
431 | int tChunkWriter::Write(const tTri& f)  |
432 | {  |
433 | if (NeedsEndianSwap)  |
434 | {  |
435 | tTri s(f);  |
436 | for (int i = 0; i < 3; i++)  |
437 | tSwapEndian(s.Index[i]);  |
438 | return Write( (void*)&s, sizeof(s) );  |
439 | }  |
440 |   |
441 | return Write( (void*)&f, sizeof(f) );  |
442 | }  |
443 |   |
444 |   |
445 | int tChunkWriter::Write(const tQuad& f)  |
446 | {  |
447 | if (NeedsEndianSwap)  |
448 | {  |
449 | tQuad s(f);  |
450 | for (int i = 0; i < 4; i++)  |
451 | tSwapEndian(s.Index[i]);  |
452 | return Write( (void*)&s, sizeof(s) );  |
453 | }  |
454 |   |
455 | return Write( (void*)&f, sizeof(f) );  |
456 | }  |
457 |   |
458 |   |
459 | int tChunkWriter::Write(const tSphere& s)  |
460 | {  |
461 | if (NeedsEndianSwap)  |
462 | {  |
463 | tSphere ss(s);  |
464 | for (int i = 0; i < 3; i++)  |
465 | tSwapEndian(ss.Center.E[i]);  |
466 | tSwapEndian(ss.Radius);  |
467 | return Write( (void*)&ss, sizeof(ss) );  |
468 | }  |
469 |   |
470 | return Write( (void*)&s, sizeof(s) );  |
471 | }  |
472 |   |
473 |   |
474 | int tChunkWriter::Write(const tVector2* data, int numItems)  |
475 | {  |
476 | if (!numItems || !data)  |
477 | return 0;  |
478 |   |
479 | if (NeedsEndianSwap)  |
480 | {  |
481 | tVec2* swappedItems = new tVec2[numItems];  |
482 | for (int i = 0; i < numItems; i++)  |
483 | for (int e = 0; e < 2; e++)  |
484 | swappedItems[i].E[e] = tGetSwapEndian(data[i].E[e]);  |
485 |   |
486 | int n = Write( (void*)swappedItems, sizeof(tVec2)*numItems );  |
487 | delete[] swappedItems;  |
488 | return n;  |
489 | }  |
490 |   |
491 | return Write( (void*)data, sizeof(tVec2)*numItems );  |
492 | }  |
493 |   |
494 |   |
495 | int tChunkWriter::Write(const tVector3* data, int numItems)  |
496 | {  |
497 | if (!numItems || !data)  |
498 | return 0;  |
499 |   |
500 | if (NeedsEndianSwap)  |
501 | {  |
502 | tVec3* swappedItems = new tVec3[numItems];  |
503 | for (int i = 0; i < numItems; i++)  |
504 | for (int e = 0; e < 3; e++)  |
505 | swappedItems[i].E[e] = tGetSwapEndian(data[i].E[e]);  |
506 |   |
507 | int n = Write( (void*)swappedItems, sizeof(tVector3)*numItems );  |
508 | delete[] swappedItems;  |
509 | return n;  |
510 | }  |
511 |   |
512 | return Write( (void*)data, sizeof(tVec3)*numItems );  |
513 | }  |
514 |   |
515 |   |
516 | int tChunkWriter::Write(const tVector4* data, int numItems)  |
517 | {  |
518 | if (!numItems || !data)  |
519 | return 0;  |
520 |   |
521 | if (NeedsEndianSwap)  |
522 | {  |
523 | tVec4* swappedItems = new tVec4[numItems];  |
524 | for (int i = 0; i < numItems; i++)  |
525 | for (int e = 0; e < 4; e++)  |
526 | swappedItems[i].E[e] = tGetSwapEndian(data[i].E[e]);  |
527 |   |
528 | int n = Write( (void*)swappedItems, sizeof(tVec4)*numItems );  |
529 | delete[] swappedItems;  |
530 | return n;  |
531 | }  |
532 |   |
533 | return Write( (void*)data, sizeof(tVec4)*numItems );  |
534 | }  |
535 |   |
536 |   |
537 | int tChunkWriter::Write(const tQuaternion* data, int numItems)  |
538 | {  |
539 | if (!numItems || !data)  |
540 | return 0;  |
541 |   |
542 | if (NeedsEndianSwap)  |
543 | {  |
544 | tQuat* swappedItems = new tQuat[numItems];  |
545 | for (int i = 0; i < numItems; i++)  |
546 | for (int e = 0; e < 4; e++)  |
547 | swappedItems[i].E[e] = tGetSwapEndian(data[i].E[e]);  |
548 |   |
549 | int n = Write( (void*)swappedItems, sizeof(tQuat)*numItems );  |
550 | delete[] swappedItems;  |
551 | return n;  |
552 | }  |
553 |   |
554 | return Write( (void*)data, sizeof(tQuat)*numItems );  |
555 | }  |
556 |   |
557 |   |
558 | int tChunkWriter::Write(const tMatrix2* data, int numItems)  |
559 | {  |
560 | if (!numItems || !data)  |
561 | return 0;  |
562 |   |
563 | if (NeedsEndianSwap)  |
564 | {  |
565 | tMat2* swappedItems = new tMat2[numItems];  |
566 | for (int i = 0; i < numItems; i++)  |
567 | for (int e = 0; e < 4; e++)  |
568 | swappedItems[i].E[e] = tGetSwapEndian(data[i].E[e]);  |
569 |   |
570 | int n = Write( (void*)swappedItems, sizeof(tMat2)*numItems );  |
571 | delete[] swappedItems;  |
572 | return n;  |
573 | }  |
574 |   |
575 | return Write( (void*)data, sizeof(tMat2)*numItems );  |
576 | }  |
577 |   |
578 |   |
579 | int tChunkWriter::Write(const tMatrix4* data, int numItems)  |
580 | {  |
581 | if (!numItems || !data)  |
582 | return 0;  |
583 |   |
584 | if (NeedsEndianSwap)  |
585 | {  |
586 | tMat4* swappedItems = new tMat4[numItems];  |
587 | for (int i = 0; i < numItems; i++)  |
588 | for (int e = 0; e < 16; e++)  |
589 | swappedItems[i].E[e] = tGetSwapEndian(data[i].E[e]);  |
590 |   |
591 | int n = Write( (void*)swappedItems, sizeof(tMat4)*numItems );  |
592 | delete[] swappedItems;  |
593 | return n;  |
594 | }  |
595 |   |
596 | return Write( (void*)data, sizeof(tMat4)*numItems );  |
597 | }  |
598 |   |
599 |   |
600 | int tChunkWriter::Write(const tEdge* data, int numItems)  |
601 | {  |
602 | if (!numItems || !data)  |
603 | return 0;  |
604 |   |
605 | if (NeedsEndianSwap)  |
606 | {  |
607 | tEdge* swappedItems = new tEdge[numItems];  |
608 | for (int i = 0; i < numItems; i++)  |
609 | for (int e = 0; e < 2; e++)  |
610 | swappedItems[i].Index[e] = tGetSwapEndian(data[i].Index[e]);  |
611 |   |
612 | int n = Write( (void*)swappedItems, sizeof(tEdge)*numItems );  |
613 | delete[] swappedItems;  |
614 | return n;  |
615 | }  |
616 |   |
617 | return Write( (void*)data, sizeof(tEdge)*numItems );  |
618 | }  |
619 |   |
620 |   |
621 | int tChunkWriter::Write(const tTri* data, int numItems)  |
622 | {  |
623 | if (!numItems || !data)  |
624 | return 0;  |
625 |   |
626 | if (NeedsEndianSwap)  |
627 | {  |
628 | tTri* swappedItems = new tTri[numItems];  |
629 | for (int i = 0; i < numItems; i++)  |
630 | for (int e = 0; e < 3; e++)  |
631 | swappedItems[i].Index[e] = tGetSwapEndian(data[i].Index[e]);  |
632 |   |
633 | int n = Write( (void*)swappedItems, sizeof(tTri)*numItems );  |
634 | delete[] swappedItems;  |
635 | return n;  |
636 | }  |
637 |   |
638 | return Write( (void*)data, sizeof(tTri)*numItems );  |
639 | }  |
640 |   |
641 |   |
642 | int tChunkWriter::Write(const tQuad* data, int numItems)  |
643 | {  |
644 | if (!numItems || !data)  |
645 | return 0;  |
646 |   |
647 | if (NeedsEndianSwap)  |
648 | {  |
649 | tQuad* swappedItems = new tQuad[numItems];  |
650 | for (int i = 0; i < numItems; i++)  |
651 | for (int e = 0; e < 4; e++)  |
652 | swappedItems[i].Index[e] = tGetSwapEndian(data[i].Index[e]);  |
653 |   |
654 | int n = Write( (void*)swappedItems, sizeof(tQuad)*numItems );  |
655 | delete[] swappedItems;  |
656 | return n;  |
657 | }  |
658 |   |
659 | return Write( (void*)data, sizeof(tQuad)*numItems );  |
660 | }  |
661 |   |
662 |   |
663 | int tChunkWriter::Write(const tSphere* data, int numItems)  |
664 | {  |
665 | if (!numItems || !data)  |
666 | return 0;  |
667 |   |
668 | if (NeedsEndianSwap)  |
669 | {  |
670 | tSphere* swappedItems = new tSphere[numItems];  |
671 | for (int i = 0; i < numItems; i++)  |
672 | {  |
673 | for (int e = 0; e < 3; e++)  |
674 | swappedItems[i].Center.E[e] = tGetSwapEndian(data[i].Center.E[e]);  |
675 | swappedItems[i].Radius = tGetSwapEndian(data[i].Radius);  |
676 | }  |
677 |   |
678 | int n = Write( (void*)swappedItems, sizeof(tSphere)*numItems );  |
679 | delete[] swappedItems;  |
680 | return n;  |
681 | }  |
682 |   |
683 | return Write( (void*)data, sizeof(tSphere)*numItems );  |
684 | }  |
685 |   |
686 |   |
687 | int tChunkWriter::Write(const void* data, const tString& layoutStr)  |
688 | {  |
689 | tString layout = layoutStr;  |
690 | if (layout.IsEmpty() || !data)  |
691 | return 0;  |
692 |   |
693 | layout.Remove(' ');  |
694 | layout.Remove('_');  |
695 | layout.Remove('-');  |
696 | layout.Replace("m4" , "v4v4v4v4" );  |
697 | layout.Replace("m2" , "v2v2" );  |
698 | layout.Replace("v4" , "4444" );  |
699 | layout.Replace("v3" , "444" );  |
700 | layout.Replace("v2" , "44" );  |
701 | layout.Replace("q" , "4444" );  |
702 | layout.Replace("f" , "4" );  |
703 | layout.Replace("d" , "8" );  |
704 |   |
705 | // Is the string wellformed?  |
706 | int strLen = 0;  |
707 | strLen += layout.CountChar('1');  |
708 | strLen += layout.CountChar('2');  |
709 | strLen += layout.CountChar('4');  |
710 | strLen += layout.CountChar('8');  |
711 | if (!strLen || (layout.Length() != strLen))  |
712 | return 0;  |
713 |   |
714 | // Now lets compute the number of data bytes.  |
715 | int numBytes = 0;  |
716 | for (int c = 0; c < strLen; c++)  |
717 | numBytes += layout[c] - '0';  |
718 |   |
719 | if (NeedsEndianSwap)  |
720 | {  |
721 | uint8* swapped = new uint8[numBytes];  |
722 | tMemcpy(swapped, data, numBytes);  |
723 | uint8* curr = swapped;  |
724 |   |
725 | for (int c = 0; c < strLen; c++)  |
726 | {  |
727 | switch (layout[c])  |
728 | {  |
729 | case '1':  |
730 | curr += 1;  |
731 | break;  |
732 |   |
733 | case '2':  |
734 | {  |
735 | uint16& d = *((uint16*)curr);  |
736 | tSwapEndian(d);  |
737 | curr += 2;  |
738 | break;  |
739 | }  |
740 |   |
741 | case '4':  |
742 | {  |
743 | uint32& d = *((uint32*)curr);  |
744 | tSwapEndian(d);  |
745 | curr += 4;  |
746 | break;  |
747 | }  |
748 |   |
749 | case '8':  |
750 | {  |
751 | uint64& d = *((uint64*)curr);  |
752 | tSwapEndian(d);  |
753 | curr += 4;  |
754 | break;  |
755 | }  |
756 | }  |
757 | }  |
758 |   |
759 | int n = Write((void*)swapped, numBytes);  |
760 | delete[] swapped;  |
761 | return n;  |
762 | }  |
763 |   |
764 | return Write((void*)data, numBytes);  |
765 | }  |
766 |   |
767 |   |
768 | void tChunkReader::Load(const tString& filename, uint8* buffer)  |
769 | {  |
770 | UnLoad();  |
771 | tFileHandle fh = tOpenFile(filename.ConstText(), "rb" );  |
772 | ReadBufferSize = tGetFileSize(fh);  |
773 | const int maxAlign = 1 << (int(tChunkWriter::Alignment::Largest) + 2);  |
774 |   |
775 | if (!buffer)  |
776 | {  |
777 | // Create a buffer big enough for the file. Make sure it is aligned.  |
778 | ReadBuffer = (uint8*)tMem::tMalloc(ReadBufferSize, maxAlign);  |
779 | IsBufferOwned = true;  |
780 | }  |
781 | else  |
782 | {  |
783 | ReadBuffer = buffer;  |
784 | }  |
785 |   |
786 | // Casting to uint32 is safe even for 64 bit pointers because maxAlign is much smaller than 2^32.  |
787 | tAssert((uint32(uint64(ReadBuffer)) % maxAlign) == 0);  |
788 | int numRead = tReadFile(fh, ReadBuffer, ReadBufferSize);  |
789 | tAssert(numRead == ReadBufferSize);  |
790 | tCloseFile(fh);  |
791 | }  |
792 |   |
793 |   |
794 | void tChunkReader::Load(uint8* buffer, int bufferSizeBytes)  |
795 | {  |
796 | UnLoad();  |
797 | const int maxAlign = 1 << (int(tChunkWriter::Alignment::Largest) + 2);  |
798 |   |
799 | tAssert((uint32(uint64(buffer)) % maxAlign) == 0);  |
800 | IsBufferOwned = false;  |
801 | ReadBufferSize = bufferSizeBytes;  |
802 | ReadBuffer = buffer;  |
803 | }  |
804 |   |
805 |   |
806 | bool tChunkReader::LoadSafe(const tString& filename)  |
807 | {  |
808 | UnLoad();  |
809 | tFileHandle fh = tOpenFile(filename.ConstText(), "rb" );  |
810 | ReadBufferSize = tGetFileSize(fh);  |
811 | if (ReadBufferSize == 0)  |
812 | {  |
813 | tCloseFile(fh);  |
814 | return false;  |
815 | }  |
816 | const int maxAlign = 1 << (int(tChunkWriter::Alignment::Largest) + 2);  |
817 |   |
818 | // Create a buffer big enough for the file. Make sure it is aligned.  |
819 | ReadBuffer = (uint8*)tMem::tMalloc(ReadBufferSize, maxAlign);  |
820 | IsBufferOwned = true;  |
821 |   |
822 | tAssert((uint32(uint64(ReadBuffer)) % maxAlign) == 0);  |
823 | int numRead = tReadFile(fh, ReadBuffer, ReadBufferSize);  |
824 | tCloseFile(fh);  |
825 | if (numRead != ReadBufferSize)  |
826 | return false;  |
827 |   |
828 | return true;  |
829 | }  |
830 |   |
831 |   |
832 | int tChunkReader::Save(const tString& filename)  |
833 | {  |
834 | if (!IsValid())  |
835 | return 0;  |
836 |   |
837 | tFileHandle chunkFile = tOpenFile(filename, "wb" );  |
838 | tAssert(chunkFile);  |
839 |   |
840 | int numWritten = tWriteFile(chunkFile, ReadBuffer, ReadBufferSize);  |
841 | tAssert(numWritten == ReadBufferSize);  |
842 |   |
843 | tCloseFile(chunkFile);  |
844 | return numWritten;  |
845 | }  |
846 |   |
847 |   |
848 | void tChunkReader::UnLoad()  |
849 | {  |
850 | // Unload anything currently maintained.  |
851 | if (IsBufferOwned && ReadBuffer)  |
852 | tMem::tFree(ReadBuffer);  |
853 |   |
854 | ReadBuffer = nullptr;  |
855 | ReadBufferSize = 0;  |
856 | IsBufferOwned = false;  |
857 | }  |
858 | |