11/* Havok Format Library
2- Copyright(C) 2016-2023 Lukas Cone
2+ Copyright(C) 2016-2025 Lukas Cone
33
44 This program is free software : you can redistribute it and / or modify
55 it under the terms of the GNU General Public License as published by
@@ -32,7 +32,9 @@ template <class C> void PtrGuard(const C *val) {
3232using ReadFunc = void (*)(BinReaderRef, hkChunk *, hkxNewHeader *);
3333
3434template <uint32 tag>
35- ReadFunc Read = [](BinReaderRef, hkChunk *, hkxNewHeader *) {};
35+ ReadFunc Read = [](BinReaderRef, hkChunk *, hkxNewHeader *) {
36+ throw std::logic_error (" Unimplemented function" );
37+ };
3638
3739template <>
3840ReadFunc Read<1 > = [](BinReaderRef rd, hkChunk *holder, hkxNewHeader *) {
@@ -41,6 +43,29 @@ ReadFunc Read<1> = [](BinReaderRef rd, hkChunk *holder, hkxNewHeader *) {
4143 rd.Skip (holder->Size ());
4244};
4345
46+ using ReadCompFunc = void (*)(BinReaderRef, hkChunk *, hkCompendiumData *);
47+
48+ template <uint32 tag>
49+ ReadCompFunc ReadComp = [](BinReaderRef, hkChunk *, hkCompendiumData *) {
50+ throw std::logic_error (" Unimplemented function" );
51+ };
52+
53+ template <>
54+ ReadCompFunc ReadComp<1 > =
55+ [](BinReaderRef rd, hkChunk *holder, hkCompendiumData *) {
56+ PtrGuard (holder);
57+
58+ rd.Skip (holder->Size ());
59+ };
60+
61+ template <>
62+ ReadFunc Read<CompileFourCC(" INDX" )> =
63+ [](BinReaderRef, hkChunk *, hkxNewHeader *) {};
64+
65+ template <>
66+ ReadCompFunc ReadComp<CompileFourCC(" TYPE" )> =
67+ [](BinReaderRef, hkChunk *, hkCompendiumData *) {};
68+
4469template <>
4570ReadFunc Read<CompileFourCC(" SDKV" )> =
4671 [](BinReaderRef rd, hkChunk *, hkxNewHeader *root) {
@@ -86,10 +111,9 @@ ReadFunc Read<CompileFourCC("DATA")> =
86111 rd.ReadContainer (root->dataBuffer , dataSize);
87112 };
88113
89- void ReadTypeNames (BinReaderRef rd, hkChunk *holder, hkxNewHeader *root ,
90- std::string &outBuffer, hkxNewHeader ::StrVec &outVec) {
114+ void ReadTypeNames (BinReaderRef rd, hkChunk *holder, std::string &outBuffer ,
115+ hkCompendiumData ::StrVec &outVec) {
91116 PtrGuard (holder);
92- PtrGuard (root);
93117
94118 const uint32 bufferSize = holder->Size ();
95119 rd.ReadContainer (outBuffer, bufferSize);
@@ -109,16 +133,22 @@ void ReadTypeNames(BinReaderRef rd, hkChunk *holder, hkxNewHeader *root,
109133}
110134
111135template <>
112- ReadFunc Read <CompileFourCC(" TSTR" )> =
113- [](BinReaderRef rd, hkChunk *holder, hkxNewHeader *root) {
114- ReadTypeNames (rd, holder, root, root ->classNamesBuffer , root->classNames );
136+ ReadCompFunc ReadComp <CompileFourCC(" TSTR" )> =
137+ [](BinReaderRef rd, hkChunk *holder, hkCompendiumData *root) {
138+ ReadTypeNames (rd, holder, root->classNamesBuffer , root->classNames );
115139 };
116140
117141template <>
118- ReadFunc Read<CompileFourCC(" FSTR" )> = [](BinReaderRef rd, hkChunk *holder,
119- hkxNewHeader *root) {
120- ReadTypeNames (rd, holder, root, root->memberNamesBuffer , root->memberNames );
121- };
142+ ReadCompFunc ReadComp<CompileFourCC(" TST1" )> = ReadComp<CompileFourCC(" TSTR" )>;
143+
144+ template <>
145+ ReadCompFunc ReadComp<CompileFourCC(" FSTR" )> =
146+ [](BinReaderRef rd, hkChunk *holder, hkCompendiumData *root) {
147+ ReadTypeNames (rd, holder, root->memberNamesBuffer , root->memberNames );
148+ };
149+
150+ template <>
151+ ReadCompFunc ReadComp<CompileFourCC(" FST1" )> = ReadComp<CompileFourCC(" FSTR" )>;
122152
123153int32 ReadCompressedInt (BinReaderRef rd) {
124154 uint8 firstInt;
@@ -146,8 +176,9 @@ int32 ReadCompressedInt(BinReaderRef rd) {
146176}
147177
148178template <>
149- ReadFunc Read<CompileFourCC(" TNAM" )> = [](BinReaderRef rd, hkChunk *holder,
150- hkxNewHeader *root) {
179+ ReadCompFunc ReadComp<CompileFourCC(" TNAM" )> = [](BinReaderRef rd,
180+ hkChunk *holder,
181+ hkCompendiumData *root) {
151182 PtrGuard (holder);
152183 PtrGuard (root);
153184
@@ -184,11 +215,12 @@ ReadFunc Read<CompileFourCC("TNAM")> = [](BinReaderRef rd, hkChunk *holder,
184215 rd.Skip (diff);
185216};
186217
187- template <> ReadFunc Read<CompileFourCC(" TNA1" )> = Read<CompileFourCC(" TNAM" )>;
218+ template <>
219+ ReadCompFunc ReadComp<CompileFourCC(" TNA1" )> = ReadComp<CompileFourCC(" TNAM" )>;
188220
189221template <>
190- ReadFunc Read <CompileFourCC(" ITEM" )> =
191- [](BinReaderRef rd, hkChunk *holder, hkxNewHeader *root) {
222+ ReadCompFunc ReadComp <CompileFourCC(" ITEM" )> =
223+ [](BinReaderRef rd, hkChunk *holder, hkCompendiumData *root) {
192224 PtrGuard (holder);
193225 PtrGuard (root);
194226
@@ -202,8 +234,9 @@ ReadFunc Read<CompileFourCC("PTCH")> =
202234 [](BinReaderRef rd, hkChunk *holder, hkxNewHeader *root) {
203235 PtrGuard (holder);
204236 PtrGuard (root);
237+ hkCompendiumData &cData = root->Compendium ();
205238
206- if (!root-> weldedClassNames .size ()) {
239+ if (!cData. weldedClassNames .size ()) {
207240 throw std::runtime_error (" File is missing type infos." );
208241 }
209242
@@ -214,7 +247,7 @@ ReadFunc Read<CompileFourCC("PTCH")> =
214247 rd.Read (classNameIndex);
215248
216249 std::string_view toclassname =
217- root-> weldedClassNames [classNameIndex - 1 ].className ;
250+ cData. weldedClassNames [classNameIndex - 1 ].className ;
218251
219252 bool isHKArray = toclassname == " hkArray" ;
220253 bool isHKRelArray = toclassname == " hkRelArray" ;
@@ -229,15 +262,15 @@ ReadFunc Read<CompileFourCC("PTCH")> =
229262 if (isHKRelArray) {
230263 uint32 *retarget =
231264 reinterpret_cast <uint32 *>(&root->dataBuffer [0 ] + cPointer);
232- const classEntryFixup &xfix = root-> classEntries [*retarget];
265+ const classEntryFixup &xfix = cData. classEntries [*retarget];
233266
234267 uint16 *relRetarget = reinterpret_cast <uint16 *>(retarget);
235268 *relRetarget = xfix.tag - cPointer;
236269 *(relRetarget + 1 ) = xfix.count ;
237270 } else {
238271 uint64 *retarget =
239272 reinterpret_cast <uint64 *>(&root->dataBuffer [0 ] + cPointer);
240- const classEntryFixup &xfix = root-> classEntries [*retarget];
273+ const classEntryFixup &xfix = cData. classEntries [*retarget];
241274
242275 *retarget =
243276 reinterpret_cast <uint64>(xfix.tag + root->dataBuffer .data ());
@@ -249,14 +282,14 @@ ReadFunc Read<CompileFourCC("PTCH")> =
249282 }
250283 }
251284
252- for (auto &f : root-> classEntries ) {
285+ for (auto &f : cData. classEntries ) {
253286 const int32 clsID = f.Size () - 1 ;
254287
255288 if (clsID < 0 ) {
256289 continue ;
257290 }
258291
259- std::string_view clName = root-> weldedClassNames [clsID].className ;
292+ std::string_view clName = cData. weldedClassNames [clsID].className ;
260293 const JenHash chash (clName);
261294 CRule rule (root->toolset , false , 8 ); // No way to detect so far
262295 IhkVirtualClass *clsn = hkVirtualClass::Create (chash, rule);
@@ -273,7 +306,32 @@ ReadFunc Read<CompileFourCC("PTCH")> =
273306 }
274307 }
275308
276- es::Dispose (root->classEntries );
309+ es::Dispose (cData.classEntries );
310+ };
311+
312+ template <>
313+ ReadFunc Read<CompileFourCC(" TCRF" )> =
314+ [](BinReaderRef rd, hkChunk *holder, hkxNewHeader *root) {
315+ PtrGuard (holder);
316+ PtrGuard (root);
317+
318+ const size_t savepos = rd.Tell ();
319+
320+ uint64 signature;
321+ rd.Read (signature);
322+
323+ const int32 diff = static_cast <int32>(holder->Size ()) -
324+ static_cast <int32>(rd.Tell () - savepos);
325+ rd.Skip (diff);
326+
327+ if (!std::holds_alternative<hkCompendium *>(root->compendium )) {
328+ throw std::runtime_error (" no external compendium to be referenced" );
329+ }
330+
331+ hkCompendium *comp = std::get<hkCompendium *>(root->compendium );
332+ if (!comp->signatures .contains (signature)) {
333+ throw std::runtime_error (" external compendium signature mismatch" );
334+ }
277335 };
278336
279337template <uint32 fourcc> constexpr auto make () {
@@ -286,59 +344,78 @@ template <uint32 fourcc> constexpr auto makeSkip() {
286344
287345static const std::map<uint32, ReadFunc> hkChunkRegistry = {
288346 makeSkip<CompileFourCC (" TAG0" )>(), //
289- makeSkip<CompileFourCC (" TPTR" )>(), //
290- makeSkip<CompileFourCC (" TPAD" )>(), //
291- makeSkip<CompileFourCC (" THSH" )>(), //
292- makeSkip<CompileFourCC (" TBOD" )>(), // TODO
293- make<CompileFourCC (" TYPE" )>(), //
294347 make<CompileFourCC (" INDX" )>(), //
295348 make<CompileFourCC (" SDKV" )>(), //
296349 make<CompileFourCC (" DATA" )>(), //
297- make<CompileFourCC (" TSTR" )>(), //
298- make<CompileFourCC (" FSTR" )>(), //
299- make<CompileFourCC (" TNAM" )>(), //
300- make<CompileFourCC (" ITEM" )>(), //
301350 make<CompileFourCC (" PTCH" )>(), //
302- make<CompileFourCC (" TNA1 " )>(), //
351+ make<CompileFourCC (" TCRF " )>(), //
303352};
304353
305- void hkChunk::Reorder () {
306- FByteswapper (sizeAndFlags);
307- sizeAndFlags = ((sizeAndFlags & 0xffffff ) - 8 ) | (sizeAndFlags & 0xff000000 );
354+ template <uint32 fourcc> constexpr auto makeComp () {
355+ return std::make_pair (fourcc, ReadComp<fourcc>);
308356}
309357
310- void hkChunk::Read (BinReaderRef rd, hkxNewHeader *root ) {
311- rd. Read (* this );
312- Reorder ();
358+ template <uint32 fourcc> constexpr auto makeCompSkip ( ) {
359+ return std::make_pair (fourcc, ReadComp< 1 > );
360+ }
313361
314- if (!hkChunkRegistry.count (tag)) {
315- auto dec = es::InvalidHeaderError::DecompileFourCC (tag, 4 );
316- printwarning (" [Havok] Unhandled tag chunk: " << dec);
317- rd.Skip (Size ());
318- return ;
319- }
362+ static const std::map<uint32, ReadCompFunc> hkCompChunkRegistry = {
363+ makeCompSkip<CompileFourCC (" TPTR" )>(), //
364+ makeCompSkip<CompileFourCC (" TPAD" )>(), //
365+ makeCompSkip<CompileFourCC (" THSH" )>(), //
366+ makeCompSkip<CompileFourCC (" TBOD" )>(), // TODO
367+ makeCompSkip<CompileFourCC (" TBDY" )>(), // TODO
368+ makeComp<CompileFourCC (" TYPE" )>(), //
369+ makeComp<CompileFourCC (" TSTR" )>(), //
370+ makeComp<CompileFourCC (" FSTR" )>(), //
371+ makeComp<CompileFourCC (" TNAM" )>(), //
372+ makeComp<CompileFourCC (" TST1" )>(), //
373+ makeComp<CompileFourCC (" FST1" )>(), //
374+ makeComp<CompileFourCC (" TNA1" )>(), //
375+ makeComp<CompileFourCC (" ITEM" )>(), //
376+ };
320377
321- hkChunkRegistry.at (tag)(rd, this , root);
378+ void hkChunk::Reorder () {
379+ FByteswapper (sizeAndFlags);
380+ sizeAndFlags = ((sizeAndFlags & 0xffffff ) - 8 ) | (sizeAndFlags & 0xff000000 );
322381}
323382
324383void hkxNewHeader::Load (BinReaderRef rd) {
325- rd.Read (static_cast <hkChunk &>(*this ));
326- Reorder ();
384+ hkChunk hdr;
385+ rd.Read (hdr);
386+ hdr.Reorder ();
327387
328- if (tag != CompileFourCC ( " TAG0 " ) ) {
329- throw es::InvalidHeaderError (tag);
388+ if (hdr. tag != HK_HEADER_TAG ) {
389+ throw es::InvalidHeaderError (hdr. tag );
330390 }
331391
332- const uint32 fileSize = Size ();
392+ const uint32 fileSize = hdr. Size ();
333393
334394 while (rd.Tell () < fileSize) {
335- hkChunk curChunk;
336- curChunk.Read (rd, this );
395+ hkChunk chunk;
396+ rd.Read (chunk);
397+ chunk.Reorder ();
398+
399+ if (hkCompChunkRegistry.count (chunk.tag )) {
400+ hkCompChunkRegistry.at (chunk.tag )(rd, &chunk, &Compendium ());
401+ continue ;
402+ }
403+
404+ if (!hkChunkRegistry.count (chunk.tag )) {
405+ auto dec = es::InvalidHeaderError::DecompileFourCC (chunk.tag , 4 );
406+ printwarning (" [Havok] Unhandled tag chunk: " << dec);
407+ rd.Skip (chunk.Size ());
408+ return ;
409+ }
410+
411+ hkChunkRegistry.at (chunk.tag )(rd, &chunk, this );
337412 }
338413}
339414
340415void hkxNewHeader::DumpClassNames (std::ostream &str) {
341- for (auto &cl : weldedClassNames) {
416+ auto &cData = Compendium ();
417+
418+ for (auto &cl : cData.weldedClassNames ) {
342419 str << cl.className ;
343420
344421 if (cl.templateArguments .size ()) {
@@ -356,3 +433,46 @@ void hkxNewHeader::DumpClassNames(std::ostream &str) {
356433 str << ' \n ' ;
357434 }
358435}
436+
437+ void hkCompendium::Load (BinReaderRef rd) {
438+ hkChunk hdr;
439+ rd.Read (hdr);
440+ hdr.Reorder ();
441+
442+ if (hdr.tag != HK_HEADER_TCM ) {
443+ throw es::InvalidHeaderError (hdr.tag );
444+ }
445+
446+ {
447+ hkChunk chunk;
448+ rd.Read (chunk);
449+ chunk.Reorder ();
450+
451+ if (chunk.tag != CompileFourCC (" TCID" )) {
452+ throw es::InvalidHeaderError (chunk.tag );
453+ }
454+
455+ const uint32 numSignatures = chunk.Size () / sizeof (uint64);
456+ std::vector<uint64> signatures;
457+
458+ rd.ReadContainer (signatures, numSignatures);
459+ this ->signatures = {signatures.begin (), signatures.end ()};
460+ }
461+
462+ const uint32 fileSize = hdr.Size ();
463+
464+ while (rd.Tell () < fileSize) {
465+ hkChunk chunk;
466+ rd.Read (chunk);
467+ chunk.Reorder ();
468+
469+ if (!hkCompChunkRegistry.count (chunk.tag )) {
470+ auto dec = es::InvalidHeaderError::DecompileFourCC (chunk.tag , 4 );
471+ printwarning (" [Havok] Unhandled tag chunk: " << dec);
472+ rd.Skip (chunk.Size ());
473+ return ;
474+ }
475+
476+ hkCompChunkRegistry.at (chunk.tag )(rd, &chunk, this );
477+ }
478+ }
0 commit comments