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