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" 
48using namespace tStd
49using namespace tSystem
50using namespace tMath
51 
52 
53void 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 
80bool 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 
109void 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 
136void 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 
228void 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 
287int 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 
327int 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 
342int 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 
357int 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 
372int 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 
387int 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 
402int 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 
417int 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 
431int 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 
445int 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 
459int 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 
474int 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 
495int 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 
516int 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 
537int 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 
558int 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 
579int 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 
600int 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 
621int 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 
642int 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 
663int 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 
687int 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 
768void 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 
794void 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 
806bool 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 
832int 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 
848void 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