Skip to content

Commit 7940cbb

Browse files
committed
add compendium support
1 parent 5a25a58 commit 7940cbb

5 files changed

Lines changed: 275 additions & 90 deletions

File tree

include/hklib/hk_packfile.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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
@@ -38,6 +38,7 @@ struct HK_EXTERN IhkPackFile {
3838

3939
static Ptr Create(const std::string &fileName);
4040
static Ptr Create(BinReaderRef_e rd);
41+
static Ptr Create(BinReaderRef_e rd, IhkPackFile *compendium);
4142

4243
void ToXML(const std::string &fileName, hkToolset toolset);
4344
void ToXML(pugi::xml_node node, hkToolset toolset);

source/format_new.cpp

Lines changed: 175 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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) {
3232
using ReadFunc = void (*)(BinReaderRef, hkChunk *, hkxNewHeader *);
3333

3434
template <uint32 tag>
35-
ReadFunc Read = [](BinReaderRef, hkChunk *, hkxNewHeader *) {};
35+
ReadFunc Read = [](BinReaderRef, hkChunk *, hkxNewHeader *) {
36+
throw std::logic_error("Unimplemented function");
37+
};
3638

3739
template <>
3840
ReadFunc 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+
4469
template <>
4570
ReadFunc 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

111135
template <>
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

117141
template <>
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

123153
int32 ReadCompressedInt(BinReaderRef rd) {
124154
uint8 firstInt;
@@ -146,8 +176,9 @@ int32 ReadCompressedInt(BinReaderRef rd) {
146176
}
147177

148178
template <>
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

189221
template <>
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

279337
template <uint32 fourcc> constexpr auto make() {
@@ -286,59 +344,78 @@ template <uint32 fourcc> constexpr auto makeSkip() {
286344

287345
static 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

324383
void 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

340415
void 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

Comments
 (0)