• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PredatorCZ / HavokLib / 89

06 Nov 2025 01:09PM UTC coverage: 63.837% (-3.2%) from 67.014%
89

push

github

PredatorCZ
s

3354 of 5254 relevant lines covered (63.84%)

129082.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

16.92
/source/format_new.cpp
1
/*  Havok Format Library
2
    Copyright(C) 2016-2025 Lukas Cone
3

4
    This program is free software : you can redistribute it and / or modify
5
    it under the terms of the GNU General Public License as published by
6
    the Free Software Foundation, either version 3 of the License, or
7
    (at your option) any later version.
8

9
    This program is distributed in the hope that it will be useful,
10
    but WITHOUT ANY WARRANTY; without even the implied warranty of
11
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
12
    GNU General Public License for more details.
13

14
    You should have received a copy of the GNU General Public License
15
    along with this program.If not, see <https://www.gnu.org/licenses/>.
16
*/
17

18
#include "format_new.hpp"
19
#include "internal/hk_internal_api.hpp"
20
#include "spike/except.hpp"
21
#include "spike/io/binreader.hpp"
22
#include "spike/io/binwritter.hpp"
23
#include "spike/master_printer.hpp"
24
#include <map>
25

26
template <class C> void PtrGuard(const C *val) {
×
27
  if (!val) {
×
28
    throw std::runtime_error("nullptr detected");
×
29
  }
30
}
31

×
32
using ReadFunc = void (*)(BinReaderRef, hkChunk *, hkxNewHeader *);
×
33

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

×
39
template <>
40
ReadFunc Read<1> = [](BinReaderRef rd, hkChunk *holder, hkxNewHeader *) {
41
  PtrGuard(holder);
×
42

×
43
  rd.Skip(holder->Size());
×
44
};
45

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

69
template <>
70
ReadFunc Read<CompileFourCC("SDKV")> =
×
71
    [](BinReaderRef rd, hkChunk *, hkxNewHeader *root) {
×
72
      PtrGuard(root);
73
      char buff[8];
×
74

75
      rd.Read(buff);
76
      buff[4] = 0;
77

78
      uint32 version = atoi(buff);
×
79

80
      auto convert = [&] {
81
        switch (version) {
82
        case 2015:
×
83
          return HK2015;
84
        case 2016:
85
          return HK2016;
86
        case 2017:
×
87
          return HK2017;
×
88
        case 2018:
89
          return HK2018;
90
        case 2019:
×
91
          return HK2019;
×
92
        default:
93
          return HKUNKVER;
×
94
        }
95
      };
×
96

×
97
      root->toolset = convert();
×
98

×
99
      if (root->toolset == HKUNKVER) {
×
100
        throw es::InvalidVersionError(version);
×
101
      }
×
102
    };
×
103

×
104
template <>
×
105
ReadFunc Read<CompileFourCC("DATA")> =
×
106
    [](BinReaderRef rd, hkChunk *holder, hkxNewHeader *root) {
×
107
      PtrGuard(holder);
×
108
      PtrGuard(root);
×
109

110
      const uint32 dataSize = holder->Size();
111
      rd.ReadContainer(root->dataBuffer, dataSize);
112
    };
×
113

114
void ReadTypeNames(BinReaderRef rd, hkChunk *holder, std::string &outBuffer,
×
115
                   hkCompendiumData::StrVec &outVec) {
×
116
  PtrGuard(holder);
117

118
  const uint32 bufferSize = holder->Size();
119
  rd.ReadContainer(outBuffer, bufferSize);
120
  char *buffer = &outBuffer[0];
121
  std::string_view lastName = buffer;
×
122

×
123
  for (uint32 t = 0; t < bufferSize; t++) {
×
124
    if (!buffer[t]) {
125
      outVec.push_back(lastName);
×
126
      lastName = buffer + t + 1;
×
127

128
      if (lastName.empty()) {
129
        break;
×
130
      }
131
    }
×
132
  }
133
}
×
134

×
135
template <>
×
136
ReadCompFunc ReadComp<CompileFourCC("TSTR")> =
×
137
    [](BinReaderRef rd, hkChunk *holder, hkCompendiumData *root) {
138
      ReadTypeNames(rd, holder, root->classNamesBuffer, root->classNames);
×
139
    };
×
140

×
141
template <>
×
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")>;
152

×
153
int32 ReadCompressedInt(BinReaderRef rd) {
×
154
  uint8 firstInt;
155
  int32 resultInt = 0;
156

157
  rd.Read(firstInt);
158

159
  const bool flag1 = (firstInt & 0x80) == 0x80;
160
  const bool flag2 = (firstInt & 0xC0) == 0xC0;
161
  const bool flag3 = (firstInt & 0xE0) == 0xE0;
×
162

×
163
  if (flag3) {
164
    rd.Read(resultInt);
165
    resultInt |= (firstInt & 0xf) << 4;
166
  } else if (flag2) {
167
    throw std::logic_error("Unhandled int compression : 0xC0!");
168
  } else if (flag1) {
×
169
    uint8 secondInt;
170
    rd.Read(secondInt);
×
171
    resultInt = secondInt | ((static_cast<int32>(firstInt) & 0xf) << 8);
172
  } else
×
173
    resultInt = firstInt;
174

×
175
  return resultInt;
×
176
}
×
177

178
template <>
×
179
ReadCompFunc ReadComp<CompileFourCC("TNAM")> = [](BinReaderRef rd,
×
180
                                                  hkChunk *holder,
×
181
                                                  hkCompendiumData *root) {
×
182
  PtrGuard(holder);
×
183
  PtrGuard(root);
×
184

185
  const size_t savepos = rd.Tell();
×
186
  const uint32 numClasses = ReadCompressedInt(rd) - 1;
×
187

188
  root->weldedClassNames.resize(numClasses);
×
189

190
  for (auto &c : root->weldedClassNames) {
×
191
    const uint32 classNameIndex = ReadCompressedInt(rd);
192
    c.className = root->classNames[classNameIndex];
193

194
    uint8 numTemplateArgs;
×
195
    rd.Read(numTemplateArgs);
196
    c.templateArguments.resize(numTemplateArgs);
197

×
198
    for (auto &t : c.templateArguments) {
×
199
      uint32 argNameIndex = ReadCompressedInt(rd);
200
      t.argName = root->classNames[argNameIndex];
×
201

×
202
      argNameIndex = ReadCompressedInt(rd);
203
      t.argType = &root->weldedClassNames[argNameIndex - 1];
×
204
    }
205
  }
×
206

×
207
  const int32 diff = static_cast<int32>(holder->Size()) -
×
208
                     static_cast<int32>(rd.Tell() - savepos);
209

210
  if (diff < 0) {
×
211
    printwarning("[Havok] TNAM chunk read too much data, possible incorrect "
×
212
                 "class map, proceeding to next chunk.");
213
  }
×
214

×
215
  rd.Skip(diff);
×
216
};
217

×
218
template <>
×
219
ReadCompFunc ReadComp<CompileFourCC("TNA1")> = ReadComp<CompileFourCC("TNAM")>;
220

221
template <>
222
ReadCompFunc ReadComp<CompileFourCC("ITEM")> =
×
223
    [](BinReaderRef rd, hkChunk *holder, hkCompendiumData *root) {
224
      PtrGuard(holder);
225
      PtrGuard(root);
×
226

×
227
      const uint32 numFixups = holder->Size() / sizeof(classEntryFixup);
228

229
      rd.ReadContainer(root->classEntries, numFixups);
230
    };
×
231

232
template <>
233
ReadFunc Read<CompileFourCC("PTCH")> =
234
    [](BinReaderRef rd, hkChunk *holder, hkxNewHeader *root) {
235
      PtrGuard(holder);
236
      PtrGuard(root);
237
      hkCompendiumData &cData = root->Compendium();
238

×
239
      if (!cData.weldedClassNames.size()) {
×
240
        throw std::runtime_error("File is missing type infos.");
×
241
      }
242

×
243
      const size_t endPos = rd.Tell() + holder->Size();
244

×
245
      while (rd.Tell() < endPos) {
246
        uint32 classNameIndex;
247
        rd.Read(classNameIndex);
248

249
        std::string_view toclassname =
×
250
            cData.weldedClassNames[classNameIndex - 1].className;
×
251

×
252
        bool isHKArray = toclassname == "hkArray";
×
253
        bool isHKRelArray = toclassname == "hkRelArray";
254

×
255
        uint32 numPointers;
×
256
        rd.Read(numPointers);
257

258
        for (uint32 t = 0; t < numPointers; t++) {
×
259
          uint32 cPointer;
260
          rd.Read(cPointer);
×
261

262
          if (isHKRelArray) {
×
263
            uint32 *retarget =
264
                reinterpret_cast<uint32 *>(&root->dataBuffer[0] + cPointer);
265
            const classEntryFixup &xfix = cData.classEntries[*retarget];
×
266

267
            uint16 *relRetarget = reinterpret_cast<uint16 *>(retarget);
×
268
            *relRetarget = xfix.tag - cPointer;
×
269
            *(relRetarget + 1) = xfix.count;
270
          } else {
271
            uint64 *retarget =
×
272
                reinterpret_cast<uint64 *>(&root->dataBuffer[0] + cPointer);
273
            const classEntryFixup &xfix = cData.classEntries[*retarget];
×
274

275
            *retarget =
×
276
                reinterpret_cast<uint64>(xfix.tag + root->dataBuffer.data());
277

×
278
            if (isHKArray) {
279
              *(retarget + 1) = xfix.count;
×
280
            }
×
281
          }
282
        }
×
283
      }
×
284

×
285
      for (auto &f : cData.classEntries) {
286
        const int32 clsID = f.Size() - 1;
287

×
288
        if (clsID < 0) {
×
289
          continue;
290
        }
×
291

×
292
        std::string_view clName = cData.weldedClassNames[clsID].className;
293
        const JenHash chash(clName);
×
294
        CRule rule(root->toolset, false, 8); // No way to detect so far
×
295
        IhkVirtualClass *clsn = hkVirtualClass::Create(chash, rule);
296
        auto cls = const_cast<hkVirtualClass *>(
297
            safe_deref_cast<const hkVirtualClass>(clsn));
298

299
        if (cls) {
300
          cls->SetDataPointer(&root->dataBuffer[0] + f.tag);
×
301
          cls->className = clName;
×
302
          cls->AddHash(clName);
303
          cls->header = root;
×
304
          root->virtualClasses.emplace_back(clsn);
×
305
          cls->Process();
306
        }
307
      }
×
308

×
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
      }
335
    };
336

×
337
template <uint32 fourcc> constexpr auto make() {
338
  return std::make_pair(fourcc, Read<fourcc>);
×
339
}
340

×
341
template <uint32 fourcc> constexpr auto makeSkip() {
342
  return std::make_pair(fourcc, Read<1>);
×
343
}
×
344

345
static const std::map<uint32, ReadFunc> hkChunkRegistry = {
346
    makeSkip<CompileFourCC("TAG0")>(), //
×
347
    make<CompileFourCC("INDX")>(),     //
×
348
    make<CompileFourCC("SDKV")>(),     //
×
349
    make<CompileFourCC("DATA")>(),     //
350
    make<CompileFourCC("PTCH")>(),     //
351
    make<CompileFourCC("TCRF")>(),     //
352
};
10✔
353

10✔
354
template <uint32 fourcc> constexpr auto makeComp() {
355
  return std::make_pair(fourcc, ReadComp<fourcc>);
2✔
356
}
2✔
357

358
template <uint32 fourcc> constexpr auto makeCompSkip() {
2✔
359
  return std::make_pair(fourcc, ReadComp<1>);
2✔
360
}
361

2✔
362
static const std::map<uint32, ReadCompFunc> hkCompChunkRegistry = {
2✔
363
    makeCompSkip<CompileFourCC("TPTR")>(), //
364
    makeCompSkip<CompileFourCC("TPAD")>(), //
2✔
365
    makeCompSkip<CompileFourCC("THSH")>(), //
2✔
366
    makeCompSkip<CompileFourCC("TBOD")>(), // TODO
367
    makeCompSkip<CompileFourCC("TBDY")>(), // TODO
2✔
368
    makeComp<CompileFourCC("TYPE")>(),     //
2✔
369
    makeComp<CompileFourCC("TSTR")>(),     //
370
    makeComp<CompileFourCC("FSTR")>(),     //
371
    makeComp<CompileFourCC("TNAM")>(),     //
2✔
372
    makeComp<CompileFourCC("TST1")>(),     //
2✔
373
    makeComp<CompileFourCC("FST1")>(),     //
374
    makeComp<CompileFourCC("TNA1")>(),     //
375
    makeComp<CompileFourCC("ITEM")>(),     //
376
};
377

378
void hkChunk::Reorder() {
379
  FByteswapper(sizeAndFlags);
380
  sizeAndFlags = ((sizeAndFlags & 0xffffff) - 8) | (sizeAndFlags & 0xff000000);
381
}
382

383
void hkxNewHeader::Load(BinReaderRef rd) {
384
  hkChunk hdr;
16✔
385
  rd.Read(hdr);
16✔
386
  hdr.Reorder();
387

2✔
388
  if (hdr.tag != HK_HEADER_TAG) {
2✔
389
    throw es::InvalidHeaderError(hdr.tag);
390
  }
2✔
391

2✔
392
  const uint32 fileSize = hdr.Size();
393

2✔
394
  while (rd.Tell() < fileSize) {
2✔
395
    hkChunk chunk;
396
    rd.Read(chunk);
2✔
397
    chunk.Reorder();
2✔
398

399
    if (hkCompChunkRegistry.count(chunk.tag)) {
2✔
400
      hkCompChunkRegistry.at(chunk.tag)(rd, &chunk, &Compendium());
2✔
401
      continue;
402
    }
2✔
403

2✔
404
    if (!hkChunkRegistry.count(chunk.tag)) {
405
      auto dec = es::InvalidHeaderError::DecompileFourCC(chunk.tag, 4);
2✔
406
      printwarning("[Havok] Unhandled tag chunk: " << dec);
2✔
407
      rd.Skip(chunk.Size());
408
      return;
2✔
409
    }
2✔
410

411
    hkChunkRegistry.at(chunk.tag)(rd, &chunk, this);
412
  }
10✔
413
}
10✔
414

415
void hkxNewHeader::DumpClassNames(std::ostream &str) {
2✔
416
  auto &cData = Compendium();
2✔
417

418
  for (auto &cl : cData.weldedClassNames) {
2✔
419
    str << cl.className;
2✔
420

421
    if (cl.templateArguments.size()) {
2✔
422
      str << '<';
2✔
423

424
      for (auto &t : cl.templateArguments) {
2✔
425
        str << "typename " << t.argName << " = " << t.argType->className
2✔
426
            << ", ";
427
      }
2✔
428

2✔
429
      str.seekp(-2, std::ios_base::cur);
430
      str << '>';
431
    }
432

433
    str << '\n';
434
  }
435
}
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
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc