• 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

55.39
/source/format_old.cpp
1
/*  Havok Format Library
2
    Copyright(C) 2016-2023 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 "spike/util/endian.hpp"
19

20
#include "fixups.hpp"
21
#include "format_old.hpp"
22
#include "hklib/hka_skeleton.hpp"
23
#include "internal/hk_internal_api.hpp"
24
#include "spike/crypto/jenkinshash.hpp"
25
#include "spike/except.hpp"
26
#include "spike/master_printer.hpp"
27
#include "spike/type/pointer.hpp"
28
#include <algorithm>
29
#include <ctype.h>
30
#include <string>
31
#include <unordered_map>
32

33
#include "spike/io/binreader.hpp"
34
#include "spike/io/binwritter.hpp"
35

36
void hkxHeader::Load(BinReaderRef_e rd) {
906✔
37
  rd.Read<hkxHeaderData>(*this);
906✔
38

39
  if (magic1 != hkMagic1) {
906✔
40
    throw es::InvalidHeaderError(magic1);
×
41
  }
42

43
  if (magic2 != hkMagic2) {
906✔
44
    throw es::InvalidHeaderError(magic2);
×
45
  }
46

47
  uint32 currentSectionID = 0;
906✔
48

49
  if (!layout.littleEndian) {
906✔
50
    SwapEndian();
453✔
51
    rd.SwapEndian(true);
453✔
52
  }
53

54
  if (maxpredicate != -1) {
906✔
55
    rd.Skip(predicateArraySizePlusPadding);
80✔
56
  }
57

58
  GenerateToolset();
906✔
59

60
  sections.resize(numSections);
906✔
61

62
  for (auto &s : sections) {
3,624✔
63
    s.header = this;
2,718✔
64
    rd.Read<hkxSectionHeaderData>(s);
2,718✔
65

66
    if (version > 9) {
2,718✔
67
      rd.Seek(16, std::ios_base::cur);
240✔
68
    }
69

70
    s.sectionID = currentSectionID;
2,718✔
71
    currentSectionID++;
2,718✔
72
  }
73

74
  for (auto &s : sections) {
3,624✔
75
    s.Load(rd);
2,718✔
76
  }
77

78
  for (auto &s : sections) {
3,624✔
79
    s.LoadBuffer(rd);
2,718✔
80
  }
81

82
  if (layout.bytesInPointer == 4) {
906✔
83
    for (auto &s : sections) {
1,816✔
84
      s.LinkBuffer86();
1,362✔
85
      s.Finalize();
1,362✔
86
    }
87
  } else {
88
    for (auto &s : sections) {
1,808✔
89
      s.LinkBuffer();
1,356✔
90
      s.Finalize();
1,356✔
91
    }
92
  }
93
}
906✔
94

95
void hkxHeader::GenerateToolset() {
906✔
96
  uint32 versions[3] = {};
906✔
97
  char *cc = contentsVersion;
906✔
98
  size_t cVer = 0;
906✔
99

100
  while (cc && *cc && cVer < 3) {
9,309✔
101
    if (isdigit(*cc)) {
8,403✔
102
      char *endPtr = nullptr;
2,718✔
103
      versions[cVer++] = std::strtol(cc, &endPtr, 10);
2,718✔
104
      cc = endPtr;
2,718✔
105
    } else {
106
      cc++;
5,685✔
107
    }
108
  }
109

110
  auto convert = [&]() {
906✔
111
    switch (versions[0]) {
906✔
112
    case 5: {
129✔
113
      switch (versions[1]) {
129✔
114
      case 0:
×
115
        return HK500;
×
116
      case 1:
×
117
        return HK510;
×
118
      case 5:
129✔
119
        return HK550;
129✔
120
      }
121
      return HKUNKVER;
×
122
    }
123

124
    case 6: {
128✔
125
      switch (versions[1]) {
128✔
126
      case 0:
×
127
        return HK600;
×
128
      case 1:
×
129
        return HK610;
×
130
      case 5:
×
131
        return HK650;
×
132
      case 6:
128✔
133
        return HK660;
128✔
134
      }
135
      return HKUNKVER;
×
136
    }
137

138
    case 7: {
128✔
139
      switch (versions[1]) {
128✔
140
      case 0:
×
141
        return HK700;
×
142
      case 1:
128✔
143
        return HK710;
128✔
144
      }
145
      return HKUNKVER;
×
146
    }
147

148
    case 2010: {
128✔
149
      switch (versions[1]) {
128✔
150
      case 1:
72✔
151
        return HK2010_1;
72✔
152
      case 2:
56✔
153
        return HK2010_2;
56✔
154
      }
155
      return HKUNKVER;
×
156
    }
157

158
    case 2011: {
152✔
159
      switch (versions[1]) {
152✔
160
      case 1:
72✔
161
        return HK2011_1;
72✔
162
      case 2:
80✔
163
        return HK2011_2;
80✔
164
      case 3:
×
165
        return HK2011_3;
×
166
      }
167
      return HKUNKVER;
×
168
    }
169

170
    case 2012: {
81✔
171
      switch (versions[1]) {
81✔
172
      case 1:
×
173
        return HK2012_1;
×
174
      case 2:
81✔
175
        return HK2012_2;
81✔
176
      }
177
      return HKUNKVER;
×
178
    }
179

180
    case 2013: {
80✔
181
      switch (versions[1]) {
80✔
182
      case 1:
80✔
183
        return HK2013_1;
80✔
184
      case 2:
×
185
        return HK2013_2;
×
186
      case 3:
×
187
        return HK2013_3;
×
188
      }
189
      return HKUNKVER;
×
190
    }
191

192
    case 2014: {
80✔
193
      switch (versions[1]) {
80✔
194
      case 1:
80✔
195
        return HK2014;
80✔
196
      }
197
      return HKUNKVER;
×
198
    }
199

200
    default:
×
201
      return HKUNKVER;
×
202
    }
203
  };
906✔
204

205
  toolset = convert();
906✔
206

207
  if (toolset == HKUNKVER) {
906✔
208
    throw es::InvalidVersionError(toolset);
×
209
  }
210
}
906✔
211

212
void hkxHeaderData::SwapEndian() {
453✔
213
  FByteswapper(version);
453✔
214
  FByteswapper(numSections);
453✔
215
  FByteswapper(contentsSectionIndex);
453✔
216
  FByteswapper(contentsSectionOffset);
453✔
217
  FByteswapper(contentsClassNameSectionIndex);
453✔
218
  FByteswapper(contentsClassNameSectionOffset);
453✔
219
  FByteswapper(flags);
453✔
220
  FByteswapper(maxpredicate);
453✔
221
  FByteswapper(predicateArraySizePlusPadding);
453✔
222
}
453✔
223

224
void hkxSectionHeader::Load(BinReaderRef_e rd) {
2,718✔
225
  rd.SetRelativeOrigin(absoluteDataStart);
2,718✔
226

227
  const int32 virtualEOF =
2,718✔
228
      (exportsOffset == -1U ? importsOffset : exportsOffset);
2,718✔
229
  const int32 circaNumLocalFixps =
2,718✔
230
      (globalFixupsOffset - localFixupsOffset) / sizeof(hkxLocalFixup);
2,718✔
231
  const int32 circaNumGlobalFixps =
2,718✔
232
      (virtualFixupsOffset - globalFixupsOffset) / sizeof(hkxGlobalFixup);
2,718✔
233
  const int32 circaNumVirtualFixps =
2,718✔
234
      (virtualEOF - virtualFixupsOffset) / sizeof(hkxVirtualFixup);
2,718✔
235

236
  localFixups.reserve(circaNumLocalFixps);
2,718✔
237
  globalFixups.reserve(circaNumGlobalFixps);
2,718✔
238
  virtualFixups.reserve(circaNumVirtualFixps);
2,718✔
239

240
  rd.Seek(localFixupsOffset);
2,718✔
241
  rd.ReadContainer(localFixups, circaNumLocalFixps);
2,718✔
242
  rd.Seek(globalFixupsOffset);
2,718✔
243
  rd.ReadContainer(globalFixups, circaNumGlobalFixps);
2,718✔
244
  rd.Seek(virtualFixupsOffset);
2,718✔
245
  rd.ReadContainer(virtualFixups, circaNumVirtualFixps);
2,718✔
246

247
  rd.ResetRelativeOrigin();
2,718✔
248
}
2,718✔
249

250
void hkxSectionHeader::LoadBuffer(BinReaderRef_e rd) {
2,718✔
251

252
  if (!bufferSize)
2,718✔
253
    return;
906✔
254

255
  rd.Seek(absoluteDataStart);
1,812✔
256
  rd.ReadContainer(buffer, localFixupsOffset);
1,812✔
257
}
258

259
void hkxSectionHeader::LinkBuffer() {
1,356✔
260
  char *sectionBuffer = &buffer[0];
1,356✔
261
  using ptrType = esPointerX64<char>;
262

263
  for (auto &lf : localFixups) {
52,732✔
264
    if (lf.pointer != -1) {
51,376✔
265
      ptrType *ptrPtr = reinterpret_cast<ptrType *>(sectionBuffer + lf.pointer);
51,360✔
266
      *ptrPtr = sectionBuffer + lf.destination;
51,360✔
267
    }
268
  }
269

270
  for (auto &gf : globalFixups) {
15,532✔
271
    if (gf.pointer != -1) {
14,176✔
272
      ptrType *ptrPtr = reinterpret_cast<ptrType *>(sectionBuffer + gf.pointer);
13,828✔
273
      *ptrPtr = &header->sections[gf.sectionid].buffer[0] + gf.destination;
13,828✔
274
    }
275
  }
276

277
  es::Dispose(localFixups);
1,356✔
278
  es::Dispose(globalFixups);
1,356✔
279
}
1,356✔
280

281
void hkxSectionHeader::LinkBuffer86() {
1,362✔
282
  char *sectionBuffer = &buffer[0];
1,362✔
283
  using ptrType = esPointerX86<char>;
284

285
  for (auto &lf : localFixups) {
52,990✔
286
    if (lf.pointer != -1) {
51,628✔
287
      ptrType *ptrPtr = reinterpret_cast<ptrType *>(sectionBuffer + lf.pointer);
51,612✔
288
      *ptrPtr = sectionBuffer + lf.destination;
51,612✔
289
    }
290
  }
291

292
  for (auto &gf : globalFixups) {
15,642✔
293
    if (gf.pointer != -1) {
14,280✔
294
      ptrType *ptrPtr = reinterpret_cast<ptrType *>(sectionBuffer + gf.pointer);
13,929✔
295
      *ptrPtr = sectionBuffer + gf.destination;
13,929✔
296
    }
297
  }
298

299
  es::Dispose(localFixups);
1,362✔
300
  es::Dispose(globalFixups);
1,362✔
301
}
1,362✔
302

303
void hkxSectionHeader::Finalize() {
2,718✔
304
  char *sectionBuffer = &buffer[0];
2,718✔
305

306
  for (auto &vf : virtualFixups) {
29,972✔
307
    if (vf.dataoffset != -1) {
27,254✔
308
      std::string_view clName =
309
          header->sections[vf.sectionid].buffer.data() + vf.classnameoffset;
27,198✔
310
      const JenHash chash(clName);
27,198✔
311
      CRule rule(header->toolset, header->layout.reusePaddingOptimization,
27,198✔
312
                 header->layout.bytesInPointer > 4);
27,198✔
313
      IhkVirtualClass *clsn = hkVirtualClass::Create(chash, rule);
27,198✔
314
      auto cls = const_cast<hkVirtualClass *>(
315
          safe_deref_cast<const hkVirtualClass>(clsn));
27,198✔
316

317
      if (cls) {
27,198✔
318
        cls->SetDataPointer(sectionBuffer + vf.dataoffset);
6,129✔
319
        cls->className = clName;
6,129✔
320
        cls->AddHash(clName);
6,129✔
321
        cls->header = header;
6,129✔
322
        if (!header->layout.littleEndian)
6,129✔
323
          cls->SwapEndian();
3,064✔
324
        virtualClasses.emplace_back(clsn);
6,129✔
325
        cls->Process();
6,129✔
326
      }
327
    }
328
  }
329

330
  es::Dispose(virtualFixups);
2,718✔
331
}
2,718✔
332

333
void hkxSectionHeader::hkxLocalFixup::SwapEndian() {
51,506✔
334
  FByteswapper(pointer);
51,506✔
335
  FByteswapper(destination);
51,506✔
336
}
51,506✔
337

338
void hkxSectionHeader::hkxGlobalFixup::SwapEndian() {
14,180✔
339
  FByteswapper(pointer);
14,180✔
340
  FByteswapper(destination);
14,180✔
341
  FByteswapper(sectionid);
14,180✔
342
}
14,180✔
343

344
void hkxSectionHeader::hkxVirtualFixup::SwapEndian() {
13,580✔
345
  FByteswapper(dataoffset);
13,580✔
346
  FByteswapper(sectionid);
13,580✔
347
  FByteswapper(classnameoffset);
13,580✔
348
}
13,580✔
349

350
void hkxSectionHeaderData::SwapEndian() {
1,359✔
351
  FByteswapper(absoluteDataStart);
1,359✔
352
  FByteswapper(localFixupsOffset);
1,359✔
353
  FByteswapper(globalFixupsOffset);
1,359✔
354
  FByteswapper(virtualFixupsOffset);
1,359✔
355
  FByteswapper(exportsOffset);
1,359✔
356
  FByteswapper(importsOffset);
1,359✔
357
  FByteswapper(bufferSize);
1,359✔
358
}
1,359✔
359

360
void hkxHeader::Save(BinWritterRef wr, const VirtualClasses &classes) const {
×
361
  if (!sections.empty()) {
×
362
    throw std::logic_error(
×
363
        "Cannot save loaded header! Use IhkPackFile::ToPackFile().");
×
364
  }
365

366
  wr.SwapEndian((layout.littleEndian != 0) != LittleEndian());
×
367
  wr.Write<hkxHeaderData>(*this);
×
368

369
  hkxSectionHeader classSection;
×
370
  std::string_view classSectionTag = "__classnames__";
×
371
  memcpy(classSection.sectionTag, classSectionTag.data(),
×
372
         classSectionTag.size() + 1);
×
373

374
  wr.Push();
×
375
  wr.Write<hkxSectionHeaderData>(classSection);
×
376

377
  if (version == 11) {
×
378
    wr.Skip(16);
×
379
  }
380

381
  hkxSectionHeader mainSection;
×
382
  std::string_view sectionTag = "__data__";
×
383
  memcpy(mainSection.sectionTag, sectionTag.data(), sectionTag.size() + 1);
×
384

385
  wr.Write<hkxSectionHeaderData>(mainSection);
×
386

387
  if (version == 11) {
×
388
    wr.Skip(16);
×
389
  }
390

391
  classSection.absoluteDataStart = static_cast<uint32>(wr.Tell());
×
392
  wr.SetRelativeOrigin(wr.Tell(), false);
×
393

394
  VirtualClasses refClasses;
×
395
  hkFixups fixups;
×
396
  std::unordered_map<const IhkVirtualClass *, IhkVirtualClass *> clsRemap;
×
397

398
  static const std::string_view reqClassNames[] = {
399
      "hkClass", "hkClassMember", "hkClassEnum", "hkClassEnumItem"};
×
400

401
  for (auto &c : reqClassNames) {
×
402
    wr.Write<uint32>(0);
×
403
    wr.Write('\t');
×
404
    wr.WriteContainer(c);
×
405
    wr.Skip(1);
×
406
  }
407

408
  CRule rule(toolset, layout.reusePaddingOptimization,
×
409
             layout.bytesInPointer > 4);
×
410

411
  for (auto &c : classes) {
×
412
    auto dc = checked_deref_cast<const hkVirtualClass>(c.get());
×
413
    auto clName = dc->GetClassName(toolset);
×
414
    auto nClass = hkVirtualClass::Create(clName, rule);
×
415

416
    if (!nClass) {
×
417
      printerror("[Havok] Cannot export unregistered class: " << clName);
×
418
      continue;
×
419
    }
420

421
    auto cls = const_cast<hkVirtualClass *>(
422
        checked_deref_cast<const hkVirtualClass>(nClass));
×
423

424
    clsRemap[c.get()] = nClass;
×
425
    cls->Reflect(c.get());
×
426

427
    if (wr.SwappedEndian()) {
×
428
      cls->SwapEndian();
×
429
    }
430

431
    refClasses.emplace_back(nClass);
×
432

433
    wr.Write<uint32>(0);
×
434
    wr.Write('\t');
×
435

436
    fixups.finals.emplace_back(wr.Tell());
×
437
    wr.WriteContainer(clName);
×
438
    wr.Skip(1);
×
439

440
    if (toolset < HK700 && clName == "hkaSkeleton") {
×
441
      wr.Write<uint32>(0);
×
442
      wr.Write('\t');
×
443

444
      const size_t numBones =
445
          checked_deref_cast<const hkaSkeleton>(c.get())->GetNumBones();
×
446

447
      for (size_t i = 0; i < numBones; i++) {
×
448
        fixups.finals.emplace_back(wr.Tell(), c.get());
×
449
      }
450
      wr.WriteT("hkaBone");
×
451
    }
452
  }
453

454
  wr.ResetRelativeOrigin(false);
×
455
  wr.ApplyPadding();
×
456
  classSection.bufferSize =
×
457
      static_cast<uint32>(wr.Tell() - classSection.absoluteDataStart);
458
  classSection.exportsOffset = classSection.bufferSize;
×
459
  classSection.globalFixupsOffset = classSection.bufferSize;
×
460
  classSection.importsOffset = classSection.bufferSize;
×
461
  classSection.localFixupsOffset = classSection.bufferSize;
×
462
  classSection.virtualFixupsOffset = classSection.bufferSize;
×
463
  wr.ApplyPadding();
×
464

465
  mainSection.absoluteDataStart = static_cast<uint32>(wr.Tell());
×
466
  wr.SetRelativeOrigin(wr.Tell(), false);
×
467

468
  size_t curFixup = 0;
×
469
  std::unordered_map<IhkVirtualClass *, size_t> savedClasses;
×
470

471
  for (auto &c : refClasses) {
×
472
    wr.ApplyPadding();
×
473
    const auto clsOffset = wr.Tell();
×
474
    savedClasses[c.get()] = clsOffset;
×
475
    fixups.finals[curFixup++].destination = clsOffset;
×
476

477
    auto cls = checked_deref_cast<const hkVirtualClass>(c.get());
×
478
    cls->Save(wr, fixups);
×
479

480
    while (curFixup < fixups.finals.size() &&
×
481
           fixups.finals[curFixup].destClass) {
×
482
      curFixup++;
×
483
    }
484
  }
485

486
  for (auto &l : fixups.locals) {
×
487
    hkxSectionHeader::hkxLocalFixup lFix;
488
    lFix.pointer = static_cast<int32>(l.strOffset);
×
489

490
    if (l.destClass) {
×
491
      auto sClass = clsRemap[l.destClass];
×
492
      lFix.destination = static_cast<int32>(savedClasses[sClass]);
×
493
    } else {
494
      lFix.destination = static_cast<int32>(l.destination);
×
495
    }
496

497
    mainSection.localFixups.push_back(lFix);
×
498
  }
499

500
  for (auto &l : fixups.finals) {
×
501
    hkxSectionHeader::hkxVirtualFixup lFix;
502
    lFix.sectionid = 0;
×
503
    lFix.classnameoffset = l.strOffset;
×
504
    lFix.dataoffset = l.destination;
×
505
    mainSection.virtualFixups.push_back(lFix);
×
506
  }
507

508
  for (auto &l : fixups.globals) {
×
509
    hkxSectionHeader::hkxGlobalFixup lFix;
510
    lFix.sectionid = 1;
×
511
    lFix.pointer = l.strOffset;
×
512
    lFix.destination = l.destination;
×
513
    mainSection.globalFixups.push_back(lFix);
×
514
  }
515

516
  wr.ApplyPadding();
×
517
  mainSection.localFixupsOffset = static_cast<int32>(wr.Tell());
×
518
  wr.WriteContainer(mainSection.localFixups);
×
519
  if (mainSection.localFixups.size() & 1) {
×
520
    wr.Write<int64>(-1);
×
521
  }
522

523
  wr.ApplyPadding();
×
524
  mainSection.globalFixupsOffset = static_cast<int32>(wr.Tell());
×
525
  wr.WriteContainer(mainSection.globalFixups);
×
526
  mainSection.virtualFixupsOffset = static_cast<int32>(wr.Tell());
×
527
  wr.WriteContainer(mainSection.virtualFixups);
×
528

529
  const size_t pad = GetPadding(wr.Tell(), 16) / 4;
×
530

531
  for (size_t p = 0; p < pad; p++) {
×
532
    wr.Write<int32>(-1);
×
533
  }
534

535
  mainSection.bufferSize = static_cast<int32>(wr.Tell());
×
536
  mainSection.exportsOffset = mainSection.bufferSize;
×
537
  mainSection.importsOffset = mainSection.bufferSize;
×
538

539
  wr.ResetRelativeOrigin(false);
×
540
  wr.Pop();
×
541
  wr.Write<hkxSectionHeaderData>(classSection);
×
542

543
  if (version == 11) {
×
544
    wr.Skip(16);
×
545
  }
546

547
  wr.Write<hkxSectionHeaderData>(mainSection);
×
548
}
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