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

PredatorCZ / RevilLib / 185

21 Apr 2026 07:59PM UTC coverage: 8.612% (-2.1%) from 10.756%
185

push

github

PredatorCZ
update sdl

3 of 459 new or added lines in 1 file covered. (0.65%)

2404 existing lines in 10 files now uncovered.

762 of 8848 relevant lines covered (8.61%)

4773.72 hits per line

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

0.13
/src/xfs.cpp
1
/*  Revil Format Library
2
    Copyright(C) 2021-2026 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 "revil/xfs.hpp"
19
#include "property.hpp"
20
#include "pugixml.hpp"
21
#include "revil/hashreg.hpp"
22
#include "spike/io/binreader.hpp"
23
#include "spike/io/binwritter.hpp"
24
#include "spike/reflect/reflector.hpp"
25
#include "spike/reflect/reflector_xml.hpp"
26
#include "spike/type/bitfield.hpp"
27
#include "spike/type/matrix44.hpp"
28
#include "spike/type/vectors_simd.hpp"
29
#include <algorithm>
30
#include <deque>
31
#include <vector>
32

33
#include "shift_jis.inl"
34

35
// #define XFS_DEBUG
36

37
/*
38
lp pc: 4
39
lp ps3: 5
40
lp2 pc: 8
41
*/
42

43
using namespace revil;
44

45
struct XFSClassMember;
46

47
struct XFSSizeAndFlag {
48
  using Size = BitMemberDecl<0, 15>;
49
  using Disable = BitMemberDecl<1, 1>;
50
  using type = BitFieldType<uint16, Size, Disable>;
51
  type data;
52

53
  const type *operator->() const { return &data; }
54
};
55

56
struct XFSClassInfo {
57
  using NumMembers = BitMemberDecl<0, 15>;
58
  using Init = BitMemberDecl<1, 1>;
59
  using Reserved = BitMemberDecl<2, 16>;
60
  using type = BitFieldType<uint32, NumMembers, Init, Reserved>;
61
  type data;
62

63
  const type *operator->() const { return &data; }
64
};
65

66
struct XFSMeta {
67
  using Active = BitMemberDecl<0, 1>;
68
  using TypeIndex = BitMemberDecl<1, 15>;
69
  using Id = BitMemberDecl<2, 16>;
70
  using type = BitFieldType<uint32, Active, TypeIndex, Id>;
71
  type data;
72

73
  const type *operator->() const { return &data; }
74
};
75

76
struct XFSHeaderBase {
77
  uint32 id;
78
  uint16 versionMajor;
79
  uint16 versionMinor;
80

UNCOV
81
  void SwapEndian() {
×
UNCOV
82
    FByteswapper(id);
×
UNCOV
83
    FByteswapper(versionMajor);
×
UNCOV
84
    FByteswapper(versionMinor);
×
85
  }
86
};
87

88
struct XFSHeaderV1 : XFSHeaderBase {
89
  uint32 numLayouts;
90
  uint32 dataStart;
91

92
  void SwapEndian() {
UNCOV
93
    FByteswapper(numLayouts);
×
UNCOV
94
    FByteswapper(dataStart);
×
95
  }
96
};
97

98
struct XFSHeaderV2 : XFSHeaderBase {
99
  uint64 unk0; // num members/strings?
100
  uint32 numLayouts;
101
  uint32 dataStart;
102
};
103

UNCOV
104
template <class PadType> struct XFSClassMemberRaw {
×
105
  std::string memberName;
106
  MtPropertyType type;
107
  uint8 flags; // alignment flags??
108
  XFSSizeAndFlag memberSize;
109
  PadType null[4];
110

UNCOV
111
  void Read(BinReaderRef_e rd) {
×
112
    uint32 memNameOffset;
UNCOV
113
    rd.Read(memNameOffset);
×
UNCOV
114
    rd.Read(type);
×
UNCOV
115
    rd.Read(flags);
×
UNCOV
116
    rd.Read(memberSize.data);
×
UNCOV
117
    rd.Read(null);
×
118
    rd.Push();
119
    rd.Seek(memNameOffset);
×
120
    rd.ReadString(memberName);
×
UNCOV
121
    memberName = sj2utf8(memberName);
×
122
    rd.Pop();
123
  }
UNCOV
124
};
×
125

UNCOV
126
template <class PtrType, bool PSN> struct XFSClassMemberV2 {
×
UNCOV
127
  std::string memberName;
×
UNCOV
128
  MtPropertyType type;
×
UNCOV
129
  uint8 flags; // alignment flags??
×
130
  uint16 memberSize;
×
131
  PtrType null[4 * (PSN + 1)];
UNCOV
132

×
UNCOV
133
  void Read(BinReaderRef_e rd) {
×
UNCOV
134
    PtrType memNameOffset;
×
135
    rd.Read(memNameOffset);
136
    rd.Read(type);
137
    rd.Read(flags);
×
138
    rd.Read(memberSize);
139
    // Padding
×
140
    if constexpr (sizeof(PtrType) == 8) {
×
141
      rd.Skip(4);
×
142
    }
×
143
    rd.Read(null);
×
144
    rd.Push();
145
    rd.Seek(memNameOffset);
×
146
    rd.ReadString(memberName);
×
147
    memberName = sj2utf8(memberName);
×
148
    rd.Pop();
149
  }
150
};
151

152
template <class PadType> struct XFSClass {
×
153
  uint32 hash;
154
  XFSClassInfo info;
155
  std::vector<XFSClassMemberRaw<PadType>> members;
156

157
  void Read(BinReaderRef_e rd) {
158
    rd.Read(hash);
159
    rd.Read(info.data);
×
160
    rd.ReadContainer(members, info->Get<XFSClassInfo::NumMembers>());
UNCOV
161
  }
×
UNCOV
162
};
×
163

×
UNCOV
164
template <class PtrType, bool PSN> struct XFSClassV2 {
×
165
  uint32 hash;
166
  std::vector<XFSClassMemberV2<PtrType, PSN>> members;
167

168
  void Read(BinReaderRef_e rd) {
169
    rd.Read(hash);
×
170

171
    // Padding
×
172
    if constexpr (sizeof(PtrType) == 8) {
×
173
      rd.Skip(4);
×
174
    }
175

UNCOV
176
    rd.ReadContainer<PtrType>(members);
×
177
  }
178
};
×
UNCOV
179

×
UNCOV
180
struct XFSClassMember {
×
UNCOV
181
  std::string name;
×
182
  MtPropertyType type;
183
  uint8 flags;
184
  uint16 size;
185

UNCOV
186
  XFSClassMember() = default;
×
187
  template <class pad_type>
188
  XFSClassMember(XFSClassMemberRaw<pad_type> &&raw)
×
189
      : name(std::move(raw.memberName)), type(raw.type), flags(raw.flags),
×
190
        size(raw.memberSize->template Get<XFSSizeAndFlag::Size>()) {
×
191
    if (raw.memberSize->template Get<XFSSizeAndFlag::Disable>()) {
192
      throw es::RuntimeError("Some bullshit");
UNCOV
193
    }
×
194
  }
195

×
UNCOV
196
  template <class pad_type, bool psn>
×
197
  XFSClassMember(XFSClassMemberV2<pad_type, psn> &&raw)
×
198
      : name(std::move(raw.memberName)), type(raw.type), flags(raw.flags),
×
199
        size(raw.memberSize) {}
200
};
201

202
REFLECT(CLASS(XFSClassMember), MEMBER(name), MEMBER(type), MEMBER(flags));
UNCOV
203

×
204
struct XFSClassDesc {
205
  uint32 hash;
×
206
  std::string_view className;
×
207
  std::vector<XFSClassMember> members;
×
208

209
  XFSClassDesc() = default;
UNCOV
210
  template <class pad_type>
×
211
  XFSClassDesc(XFSClass<pad_type> &&raw) : hash(raw.hash) {
212
    members.reserve(raw.members.size());
×
UNCOV
213

×
214
    std::transform(std::make_move_iterator(raw.members.begin()),
×
215
                   std::make_move_iterator(raw.members.end()),
×
216
                   std::back_inserter(members),
217
                   [](auto &&item) { return std::move(item); });
218
  }
219

UNCOV
220
  template <class PtrType, bool PSN>
×
221
  XFSClassDesc(XFSClassV2<PtrType, PSN> &&raw) : hash(raw.hash) {
222
    members.reserve(raw.members.size());
×
223

×
224
    std::transform(std::make_move_iterator(raw.members.begin()),
×
225
                   std::make_move_iterator(raw.members.end()),
226
                   std::back_inserter(members),
UNCOV
227
                   [](auto &&item) { return std::move(item); });
×
228
  }
229

×
UNCOV
230
  void ToXML(pugi::xml_node node) const;
×
231
};
×
232

×
233
void XFSClassDesc::ToXML(pugi::xml_node node) const {
234
  auto cNode = node.append_child("class");
235

236
  if (className.empty()) {
UNCOV
237
    char buffer[0x10]{};
×
238
    snprintf(buffer, sizeof(buffer), "%X", hash);
239
    cNode.append_attribute("hash").set_value(buffer);
×
240
  } else {
×
241
    std::string resNme(className);
×
242
    cNode.append_attribute("name").set_value(resNme.c_str());
243
  }
244

245
  for (auto &m : members) {
246
    ReflectorWrap<const XFSClassMember> refl(m);
×
247
    auto mNode = cNode.append_child("member");
248
    ReflectorXMLUtil::Save(refl, mNode,
249
                           {ReflectorXMLUtil::Flags_StringAsAttribute});
250
  }
UNCOV
251
}
×
UNCOV
252

×
253
struct XFSDataResource {
×
UNCOV
254
  std::string type;
×
255
  std::string file;
256

×
257
  void Read(BinReaderRef_e rd) {
×
258
    uint8 numStrings;
×
UNCOV
259
    rd.Read(numStrings); // ctype?
×
260

UNCOV
261
    if (numStrings != 2) {
×
UNCOV
262
      throw es::ImplementationError("Unexpected number!");
×
263
    }
×
UNCOV
264

×
265
    rd.ReadString(type); // rtype?
266
    rd.ReadString(file); // path?
267
  }
UNCOV
268
};
×
269

270
struct XFSData {
271
  union TypeData {
272
    bool asBool;
×
UNCOV
273
    int8 asInt8;
×
274
    uint8 asUInt8;
275
    int16 asInt16;
276
    uint16 asUInt16;
277
    int32 asInt32;
278
    uint32 asUInt32;
279
    int64 asInt64;
280
    uint64 asUInt64;
×
281
    Vector2 asVector2;
282
    Vector asVector3;
×
283
    Vector4A16 asVector4;
×
284
    IVector4A16 asIVector4;
285
    IVector2 asIVector2;
286
    UIVector2 asUIVector2;
287
    IVector asIVector3;
288
    void *asPointer;
289
    UCVector4 asColor;
290
    float asFloat;
×
291
    double asDouble;
UNCOV
292
    char raw[sizeof(Vector4A16)];
×
UNCOV
293

×
294
    TypeData() { memset(raw, 0, sizeof(raw)); }
295
  };
296

297
  template <class type> type *AllocArray(size_t numItems) {
298
    const size_t allocSize = sizeof(type) * numItems;
299
    mustFree = Free_Free;
UNCOV
300
    auto value = malloc(allocSize);
×
301
    data.asPointer = value;
302
    return static_cast<type *>(value);
303
  }
UNCOV
304
  template <class type> type *AllocClass() {
×
305
    mustFree = Free_DeleteSingle;
306
    auto value = new type();
307
    data.asPointer = value;
308
    return value;
309
  }
310
  template <class type> type *AllocClasses(size_t numItems) {
311
    mustFree = Free_DeleteArray;
UNCOV
312
    auto value = new type[numItems]();
×
UNCOV
313
    data.asPointer = value;
×
UNCOV
314
    return value;
×
UNCOV
315
  }
×
316
  void SetString(std::string_view sw) {
×
317
    if (sw.size() < sizeof(data.raw)) {
318
      memcpy(data.raw, sw.data(), sw.size());
319
      stringInRaw = true;
×
UNCOV
320
    } else {
×
UNCOV
321
      memcpy(AllocArray<char>(sw.size() + 1), sw.data(), sw.size() + 1);
×
UNCOV
322
    }
×
UNCOV
323
  }
×
324

325
  const char *AsString() const {
326
    return stringInRaw ? data.raw : static_cast<const char *>(data.asPointer);
×
UNCOV
327
  }
×
UNCOV
328

×
UNCOV
329
  XFSData(XFSData &&o)
×
330
      : rtti(o.rtti), numItems(o.numItems), stringInRaw(o.stringInRaw),
×
331
        mustFree(o.mustFree), data(o.data) {
332
    o.mustFree = Free_None;
333
  }
334
  XFSData() = default;
UNCOV
335
  XFSData(const XFSData &) = delete;
×
UNCOV
336

×
UNCOV
337
  ~XFSData() {
×
338
    switch (mustFree) {
339
    case Free_Free:
340
      free(data.asPointer);
2✔
341
      break;
342
    case Free_DeleteSingle: {
×
343
      switch (rtti->type) {
344
      case MtPropertyType::custom:
345
        delete static_cast<XFSDataResource *>(data.asPointer);
346
        break;
347
      case MtPropertyType::matrix44:
348
        delete static_cast<es::Matrix44 *>(data.asPointer);
349
        break;
×
UNCOV
350

×
351
      default:
352
        break;
×
353
      }
354
      break;
355
    }
356
    case Free_DeleteArray: {
UNCOV
357
      switch (rtti->type) {
×
UNCOV
358
      case MtPropertyType::custom:
×
359
        delete[] static_cast<XFSDataResource *>(data.asPointer);
UNCOV
360
        break;
×
361
      case MtPropertyType::matrix44:
362
        delete[] static_cast<es::Matrix44 *>(data.asPointer);
363
        break;
364

UNCOV
365
      default:
×
UNCOV
366
        break;
×
367
      }
368
      break;
×
369
    }
370

371
    default:
372
      break;
373
    }
374
  }
375

×
376
  XFSClassMember *rtti = nullptr;
×
377
  uint32 numItems = 0;
378

×
379
private:
380
  bool stringInRaw = false;
381

382
  enum FreeType : uint8 {
383
    Free_None,
×
384
    Free_Free,
×
385
    Free_DeleteSingle,
386
    Free_DeleteArray,
×
387
  };
388
  FreeType mustFree = Free_None;
389

390
public:
391
  TypeData data;
×
392
};
×
393

394
struct XFSClassData {
×
395
  std::vector<XFSData> members;
396
  XFSClassDesc *rtti = nullptr;
397
};
398

UNCOV
399
class revil::XFSImpl {
×
UNCOV
400
public:
×
401
  std::vector<XFSClassDesc> rtti;
402
  std::deque<XFSClassData> dataStore;
×
403
  XFSClassData *root;
404

405
  template <class PtrType>
406
  void ReadData(BinReaderRef_e rd, XFSClassData **root = nullptr);
UNCOV
407
  void ToXML(const XFSClassData &item, pugi::xml_node node);
×
UNCOV
408
  void ToXML(pugi::xml_node node);
×
409
  void RTTIToXML(pugi::xml_node node);
410
  void Load(BinReaderRef_e rd, bool openEnded);
×
411
};
412

413
XFS::XFS() : pi(std::make_unique<XFSImpl>()) {}
414
XFS::~XFS() = default;
415

416
void XFS::Load(BinReaderRef_e rd, bool openEnded) { pi->Load(rd, openEnded); }
417

418
void XFS::ToXML(pugi::xml_node node) const { pi->ToXML(node); }
UNCOV
419

×
420
void XFS::RTTIToXML(pugi::xml_node node) const { pi->RTTIToXML(node); }
×
421

UNCOV
422
template <class PtrType>
×
UNCOV
423
void XFSImpl::ReadData(BinReaderRef_e rd, XFSClassData **root) {
×
UNCOV
424
  XFSMeta meta;
×
425
  rd.Read(meta.data);
×
426

UNCOV
427
  if (!meta->Get<XFSMeta::Active>()) {
×
428
    return;
×
429
  }
430

UNCOV
431
  PtrType chunkSize;
×
432
  const size_t strBegin = rd.Tell();
433
  rd.Read(chunkSize);
×
434

×
435
  auto &&desc = rtti.at(meta->Get<XFSMeta::TypeIndex>());
436
  XFSClassData classData;
437
  classData.rtti = &desc;
438

UNCOV
439
  for (auto &d : desc.members) {
×
440
    XFSData cType;
441
    cType.rtti = &d;
442
    rd.Read(cType.numItems);
UNCOV
443

×
444
    if (cType.numItems == 1) {
445
      switch (d.type) {
446
      case MtPropertyType::bool_:
UNCOV
447
      case MtPropertyType::s8_:
×
448
      case MtPropertyType::u8_:
×
449
        rd.Read(cType.data.asUInt8);
450
        break;
451
      case MtPropertyType::s16_:
×
UNCOV
452
      case MtPropertyType::u16_:
×
453
        rd.Read(cType.data.asUInt16);
454
        break;
455
      case MtPropertyType::f32_:
456
      case MtPropertyType::s32_:
457
      case MtPropertyType::u32_:
458
        rd.Read(cType.data.asUInt32);
459
        break;
460
      case MtPropertyType::s64_:
461
      case MtPropertyType::u64_:
462
        rd.Read(cType.data.asUInt64);
463
        break;
464
      case MtPropertyType::point:
465
      case MtPropertyType::size:
466
      case MtPropertyType::float2:
467
        rd.Read(cType.data.asVector2);
468
        break;
469
      case MtPropertyType::float3:
470
        rd.Read(cType.data.asVector3);
471
        break;
472
      case MtPropertyType::vector4:
473
      case MtPropertyType::float4:
474
      case MtPropertyType::vector3:
475
        rd.Read(cType.data.asVector4);
476
        break;
477
      case MtPropertyType::rect:
478
        rd.Read(cType.data.asIVector4);
479
        break;
UNCOV
480
      case MtPropertyType::color:
×
481
        rd.Read(cType.data.asColor);
482
        break;
483
      case MtPropertyType::string_:
UNCOV
484
      case MtPropertyType::cstring: {
×
UNCOV
485
        std::string temp;
×
UNCOV
486
        rd.ReadString(temp);
×
UNCOV
487
        cType.SetString(temp);
×
488
        break;
489
      }
UNCOV
490
      case MtPropertyType::matrix44:
×
UNCOV
491
        rd.Read(*cType.AllocClass<es::Matrix44>());
×
UNCOV
492
        break;
×
UNCOV
493
      case MtPropertyType::class_:
×
UNCOV
494
      case MtPropertyType::classref:
×
495
        ReadData<PtrType>(
UNCOV
496
            rd, reinterpret_cast<XFSClassData **>(&cType.data.asPointer));
×
UNCOV
497
        break;
×
UNCOV
498
      case MtPropertyType::custom:
×
UNCOV
499
        rd.Read(*cType.AllocClass<XFSDataResource>());
×
UNCOV
500
        break;
×
501
      default:
UNCOV
502
        throw std::runtime_error("Undefined type at: " +
×
UNCOV
503
                                 std::to_string(rd.Tell()));
×
UNCOV
504
      }
×
UNCOV
505
    } else {
×
506
      switch (d.type) {
×
507
      case MtPropertyType::bool_:
UNCOV
508
      case MtPropertyType::s8_:
×
UNCOV
509
      case MtPropertyType::u8_: {
×
510
        char *adata = cType.AllocArray<char>(cType.numItems);
×
511
        rd.ReadBuffer(adata, cType.numItems);
×
512
        break;
×
513
      }
UNCOV
514
      case MtPropertyType::s16_:
×
UNCOV
515
      case MtPropertyType::u16_: {
×
516
        uint16 *adata = cType.AllocArray<uint16>(cType.numItems);
×
517
        for (size_t i = 0; i < cType.numItems; i++) {
×
518
          rd.Read(*adata++);
519
        }
×
520
        break;
521
      }
522
      case MtPropertyType::f32_:
523
      case MtPropertyType::s32_:
524
      case MtPropertyType::u32_: {
×
525
        uint32 *adata = cType.AllocArray<uint32>(cType.numItems);
526
        for (size_t i = 0; i < cType.numItems; i++) {
527
          rd.Read(*adata++);
528
        }
×
529
        break;
×
530
      }
×
531
      case MtPropertyType::s64_:
532
      case MtPropertyType::u64_: {
×
533
        uint64 *adata = cType.AllocArray<uint64>(cType.numItems);
534
        for (size_t i = 0; i < cType.numItems; i++) {
535
          rd.Read(*adata++);
×
536
        }
×
537
        break;
×
538
      }
×
UNCOV
539
      case MtPropertyType::point:
×
540
      case MtPropertyType::size: {
×
541
        Vector2 *adata = cType.AllocArray<Vector2>(cType.numItems);
×
542
        for (size_t i = 0; i < cType.numItems; i++) {
×
543
          rd.Read(*adata++);
×
544
        }
545
        break;
×
UNCOV
546
      }
×
547
      case MtPropertyType::vector3: {
548
        Vector2 *adata = cType.AllocArray<Vector2>(cType.numItems);
549
        for (size_t i = 0; i < cType.numItems; i++) {
550
          rd.Read(*adata++);
551
        }
552
        break;
553
      }
554
      case MtPropertyType::vector4:
×
555
      case MtPropertyType::float4: {
×
556
        Vector4A16 *adata = cType.AllocArray<Vector4A16>(cType.numItems);
×
UNCOV
557
        for (size_t i = 0; i < cType.numItems; i++) {
×
558
          rd.Read(*adata++);
UNCOV
559
        }
×
UNCOV
560
        break;
×
561
      }
562
      case MtPropertyType::color: {
563
        const size_t alocSize = cType.numItems * sizeof(UCVector4);
564
        char *adata = cType.AllocArray<char>(alocSize);
565
        rd.ReadBuffer(adata, alocSize);
566
        break;
567
      }
568
      case MtPropertyType::string_: {
569
        throw es::RuntimeError("Array string!");
570
      }
571
      case MtPropertyType::matrix44: {
572
        es::Matrix44 *adata = cType.AllocClasses<es::Matrix44>(cType.numItems);
573
        for (size_t i = 0; i < cType.numItems; i++) {
574
          rd.Read(*adata++);
575
        }
576
        break;
577
      }
578
      case MtPropertyType::class_:
579
      case MtPropertyType::classref: {
580
        auto adata = cType.AllocArray<XFSClassData *>(cType.numItems);
581
        for (size_t i = 0; i < cType.numItems; i++) {
582
          ReadData<PtrType>(rd, adata++);
583
        }
584
        break;
585
      }
586
      default:
587
        throw std::runtime_error("Undefined type at: " +
588
                                 std::to_string(rd.Tell()));
589
      }
590
    }
591

UNCOV
592
    classData.members.emplace_back(std::move(cType));
×
593
  }
594

595
  dataStore.emplace_back(std::move(classData));
596

UNCOV
597
  if (rd.Tell() != strBegin + chunkSize) {
×
598
    throw es::RuntimeError("Chunk size mismatch!");
599
  }
600

601
  if (root) {
602
    *root = &dataStore.back();
603
  }
604
}
605

606
void XMLSetType(const XFSClassData &item, pugi::xml_node node) {
607
  auto attr = node.append_attribute("type");
608

609
  if (item.rtti->className.empty()) {
610
    char buffer[0x10];
UNCOV
611
    snprintf(buffer, sizeof(buffer), "0x%X", item.rtti->hash);
×
UNCOV
612
    attr.set_value(buffer);
×
613
    return;
UNCOV
614
  }
×
615

UNCOV
616
  std::string resNme(item.rtti->className);
×
617
  attr.set_value(resNme.c_str());
618
}
×
619

620
void XFSImpl::RTTIToXML(pugi::xml_node node) {
UNCOV
621
  for (auto &c : rtti) {
×
622
    c.ToXML(node);
623
  }
×
624
}
UNCOV
625

×
UNCOV
626
void XFSImpl::ToXML(const XFSClassData &item, pugi::xml_node node) {
×
627
  for (auto &m : item.members) {
628
    auto name = PropType(m.rtti->type);
629

630
    if (m.numItems > 1) {
UNCOV
631
      auto cNode = node.append_child("array");
×
632
      cNode.append_attribute("name").set_value(m.rtti->name.data());
UNCOV
633
      cNode.append_attribute("type").set_value(name);
×
UNCOV
634
      cNode.append_attribute("count").set_value(m.numItems);
×
UNCOV
635

×
636
      switch (m.rtti->type) {
637
      case MtPropertyType::class_:
×
638
      case MtPropertyType::classref: {
UNCOV
639
        auto adata =
×
640
            reinterpret_cast<const XFSClassData *const *>(m.data.asPointer);
×
641
        for (size_t i = 0; i < m.numItems; i++) {
642
          auto aNode = cNode.append_child(name);
×
UNCOV
643
          auto found = std::find_if(
×
644
              dataStore.begin(), dataStore.end(),
645
              [adata, i](auto &value) { return &value == adata[i]; });
646

647
          if (!es::IsEnd(dataStore, found)) {
648
            XMLSetType(*found, aNode);
649
            ToXML(*found, aNode);
×
650
          }
651
        }
×
652
        break;
UNCOV
653
      }
×
654
      case MtPropertyType::bool_: {
655
        auto adata = reinterpret_cast<const bool *>(m.data.asPointer);
UNCOV
656

×
657
        for (size_t i = 0; i < m.numItems; i++) {
UNCOV
658
          auto aNode = cNode.append_child(name);
×
659
          aNode.append_attribute("value").set_value(adata[i]);
660
        }
×
661
        break;
UNCOV
662
      }
×
663
      case MtPropertyType::u8_: {
664
        auto adata = reinterpret_cast<const uint8 *>(m.data.asPointer);
665

×
666
        for (size_t i = 0; i < m.numItems; i++) {
UNCOV
667
          auto aNode = cNode.append_child(name);
×
668
          aNode.append_attribute("value").set_value(adata[i]);
×
669
        }
UNCOV
670
        break;
×
671
      }
672
      case MtPropertyType::s8_: {
UNCOV
673
        auto adata = reinterpret_cast<const int8 *>(m.data.asPointer);
×
674

675
        for (size_t i = 0; i < m.numItems; i++) {
×
UNCOV
676
          auto aNode = cNode.append_child(name);
×
677
          aNode.append_attribute("value").set_value(adata[i]);
678
        }
679
        break;
680
      }
681
      case MtPropertyType::s32_: {
682
        auto adata = reinterpret_cast<const int32 *>(m.data.asPointer);
683

684
        for (size_t i = 0; i < m.numItems; i++) {
×
UNCOV
685
          auto aNode = cNode.append_child(name);
×
686
          aNode.append_attribute("value").set_value(adata[i]);
687
        }
688
        break;
×
UNCOV
689
      }
×
690
      case MtPropertyType::u32_: {
691
        auto adata = reinterpret_cast<const uint32 *>(m.data.asPointer);
×
692

693
        for (size_t i = 0; i < m.numItems; i++) {
×
694
          auto aNode = cNode.append_child(name);
695
          aNode.append_attribute("value").set_value(adata[i]);
696
        }
×
UNCOV
697
        break;
×
698
      }
UNCOV
699
      case MtPropertyType::f32_: {
×
700
        auto adata = reinterpret_cast<const float *>(m.data.asPointer);
×
701

702
        for (size_t i = 0; i < m.numItems; i++) {
703
          auto aNode = cNode.append_child(name);
UNCOV
704
          aNode.append_attribute("value").set_value(adata[i]);
×
UNCOV
705
        }
×
706
        break;
707
      }
UNCOV
708

×
709
      case MtPropertyType::color: {
710
        auto adata = reinterpret_cast<const UCVector4 *>(m.data.asPointer);
711

UNCOV
712
        for (size_t i = 0; i < m.numItems; i++) {
×
713
          auto aNode = cNode.append_child(name);
714
          aNode.append_attribute("r").set_value(adata[i].X);
×
UNCOV
715
          aNode.append_attribute("g").set_value(adata[i].Y);
×
716
          aNode.append_attribute("b").set_value(adata[i].Z);
×
717
          aNode.append_attribute("a").set_value(adata[i].W);
718
        }
719
        break;
UNCOV
720
      }
×
721
      default:
722
        throw es::RuntimeError("Unhandled xml array type");
UNCOV
723
      }
×
724
    } else if (m.numItems == 1) {
×
725
      auto cNode = node.append_child(name);
×
726
      cNode.append_attribute("name").set_value(m.rtti->name.data());
727
      auto value = cNode.append_attribute("value");
728

729
      switch (m.rtti->type) {
×
730
      case MtPropertyType::bool_:
UNCOV
731
        value.set_value(m.data.asBool);
×
UNCOV
732
        break;
×
733
      case MtPropertyType::s8_:
×
734
        value.set_value(m.data.asInt8);
735
        break;
736
      case MtPropertyType::s16_:
737
        value.set_value(m.data.asInt16);
×
738
        break;
739
      case MtPropertyType::s32_:
×
740
        value.set_value(m.data.asInt32);
×
741
        break;
×
742
      case MtPropertyType::s64_:
743
        value.set_value(m.data.asInt64);
744
        break;
745
      case MtPropertyType::u8_:
×
UNCOV
746
        value.set_value(m.data.asUInt8);
×
UNCOV
747
        break;
×
748
      case MtPropertyType::u16_:
×
749
        value.set_value(m.data.asUInt16);
750
        break;
751
      case MtPropertyType::u32_:
UNCOV
752
        value.set_value(m.data.asUInt32);
×
753
        break;
754
      case MtPropertyType::u64_:
×
UNCOV
755
        value.set_value(m.data.asUInt64);
×
756
        break;
×
757
      case MtPropertyType::string_:
758
      case MtPropertyType::cstring:
759
        value.set_value(m.AsString());
UNCOV
760
        break;
×
UNCOV
761
      case MtPropertyType::color:
×
762
        value.set_name("r");
763
        value.set_value(m.data.asColor.X);
764
        cNode.append_attribute("g").set_value(m.data.asColor.Y);
765
        cNode.append_attribute("b").set_value(m.data.asColor.Z);
766
        cNode.append_attribute("a").set_value(m.data.asColor.W);
×
UNCOV
767
        break;
×
768
      case MtPropertyType::f32_:
UNCOV
769
        value.set_value(m.data.asFloat);
×
770
        break;
×
771
      case MtPropertyType::point:
×
772
        value.set_name("x");
×
773
        value.set_value(m.data.asIVector2.X);
774
        cNode.append_attribute("y").set_value(m.data.asIVector2.Y);
775
        break;
UNCOV
776
      case MtPropertyType::size:
×
777
        value.set_name("w");
UNCOV
778
        value.set_value(m.data.asUIVector2.X);
×
779
        cNode.append_attribute("h").set_value(m.data.asUIVector2.Y);
×
780
        break;
×
781
      case MtPropertyType::vector2:
782
        value.set_name("x");
783
        value.set_value(m.data.asVector2.X);
UNCOV
784
        cNode.append_attribute("y").set_value(m.data.asVector2.Y);
×
785
        break;
×
786
      case MtPropertyType::vector3:
787
        value.set_name("x");
788
        value.set_value(m.data.asVector3.X);
789
        cNode.append_attribute("y").set_value(m.data.asVector3.Y);
UNCOV
790
        cNode.append_attribute("z").set_value(m.data.asVector3.Z);
×
791
        break;
792
      case MtPropertyType::vector4:
UNCOV
793
      case MtPropertyType::float4:
×
794
        value.set_name("x");
795
        value.set_value(m.data.asVector4.X);
×
796
        cNode.append_attribute("y").set_value(m.data.asVector4.Y);
×
797
        cNode.append_attribute("z").set_value(m.data.asVector4.Z);
798
        cNode.append_attribute("w").set_value(m.data.asVector4.W);
UNCOV
799
        break;
×
UNCOV
800
      case MtPropertyType::rect:
×
801
        value.set_name("x0");
802
        value.set_value(m.data.asIVector4.X);
803
        cNode.append_attribute("y0").set_value(m.data.asIVector4.Y);
×
804
        cNode.append_attribute("x1").set_value(m.data.asIVector4.Z);
805
        cNode.append_attribute("y1").set_value(m.data.asIVector4.W);
×
806
        break;
UNCOV
807
      case MtPropertyType::class_:
×
UNCOV
808
      case MtPropertyType::classref: {
×
809
        auto found =
810
            std::find_if(dataStore.begin(), dataStore.end(), [&m](auto &value) {
811
              return &value == m.data.asPointer;
812
            });
UNCOV
813
        cNode.remove_attribute(value);
×
814

815
        if (!es::IsEnd(dataStore, found)) {
×
UNCOV
816
          XMLSetType(*found, cNode);
×
UNCOV
817
          ToXML(*found, cNode);
×
818
        }
UNCOV
819
        break;
×
820
      }
821
      case MtPropertyType::custom: {
×
UNCOV
822
        auto adata = static_cast<const XFSDataResource *>(m.data.asPointer);
×
823
        value.set_name("type");
824
        value.set_value(adata->type.data());
×
825
        cNode.append_attribute("value").set_value(adata->file.data());
×
826
        break;
827
      }
828

829
      case MtPropertyType::matrix44: {
830
        auto adata = static_cast<const es::Matrix44 *>(m.data.asPointer);
UNCOV
831
        value.set_name("m00");
×
832
        value.set_value(adata->r1().x);
833
        cNode.append_attribute("m01").set_value(adata->r1().y);
×
834
        cNode.append_attribute("m02").set_value(adata->r1().z);
UNCOV
835
        cNode.append_attribute("m03").set_value(adata->r1().w);
×
836

837
        cNode.append_attribute("m10").set_value(adata->r2().x);
838
        cNode.append_attribute("m11").set_value(adata->r2().y);
×
839
        cNode.append_attribute("m12").set_value(adata->r2().z);
840
        cNode.append_attribute("m13").set_value(adata->r2().w);
×
841

842
        cNode.append_attribute("m20").set_value(adata->r3().x);
×
843
        cNode.append_attribute("m21").set_value(adata->r3().y);
844
        cNode.append_attribute("m22").set_value(adata->r3().z);
×
845
        cNode.append_attribute("m23").set_value(adata->r3().w);
846

847
        cNode.append_attribute("m30").set_value(adata->r4().x);
×
848
        cNode.append_attribute("m31").set_value(adata->r4().y);
849
        cNode.append_attribute("m32").set_value(adata->r4().z);
×
850
        cNode.append_attribute("m33").set_value(adata->r4().w);
×
851
        break;
UNCOV
852
      }
×
853
      default:
854
        throw es::RuntimeError("Unhandled xml type");
UNCOV
855
      }
×
856
    }
UNCOV
857
  }
×
858
}
×
859

860
void XFSImpl::ToXML(pugi::xml_node node) {
861
  auto rNode = node.append_child("class");
862
  auto &&rootData = *root;
863
  XMLSetType(rootData, rNode);
864
  ToXML(rootData, rNode);
865
}
UNCOV
866

×
867
#ifdef XFS_DEBUG
×
868
std::map<uint32, XFSClassDesc> rttiStore;
869
#endif
UNCOV
870

×
UNCOV
871
static constexpr uint32 XFSID = CompileFourCC("XFS");
×
872
static constexpr uint32 XFSIDBE = CompileFourCC("\0SFX");
UNCOV
873

×
874
template <class PtrType> void Load(XFSImpl &main, BinReaderRef_e rd) {
875
  XFSHeaderV1 header;
×
876
  rd.Read(header);
877
  rd.SetRelativeOrigin(rd.Tell(), false);
UNCOV
878
  std::vector<uint32> layoutOffsets;
×
879
  std::vector<XFSClass<PtrType>> layouts;
×
880
  rd.ReadContainer(layoutOffsets, header.numLayouts);
881
  rd.ReadContainer(layouts, header.numLayouts);
×
882
  rd.Seek(header.dataStart);
×
883

884
  std::transform(std::make_move_iterator(layouts.begin()),
885
                 std::make_move_iterator(layouts.end()),
UNCOV
886
                 std::back_inserter(main.rtti), [](auto &&item) {
×
UNCOV
887
                   if (item.info->template Get<XFSClassInfo::Init>()) {
×
888
                     throw es::RuntimeError("Some bullshit");
889
                   }
890

×
891
                   return std::move(item);
892
                 });
893
}
894

×
895
template <class PtrType>
UNCOV
896
void LoadV2(XFSImpl &main, BinReaderRef_e rd, XFSHeaderV2 &header) {
×
897
  std::vector<PtrType> layoutOffsets;
×
UNCOV
898
  rd.ReadContainer(layoutOffsets, header.numLayouts);
×
899

900
  // Determine member padding
901
  rd.Push();
902
  rd.Skip(sizeof(PtrType));
×
903
  PtrType numMembers;
904
  rd.Read(numMembers);
905
  const size_t memberBegin = rd.Tell();
×
906

×
UNCOV
907
  PtrType nameOffset;
×
908
  rd.Read(nameOffset);
909
  rd.Pop();
910

911
  const size_t expectedEnd =
×
912
      layoutOffsets.size() > 1 ? layoutOffsets.at(1) : nameOffset;
UNCOV
913
  const size_t memberSize = (expectedEnd - memberBegin) / numMembers;
×
914

×
UNCOV
915
  constexpr size_t singleMemberSize = sizeof(PtrType) * 6;
×
916
  constexpr size_t singleMemberSizePSN = sizeof(PtrType) * 10;
917

918
  if (memberSize == singleMemberSize) {
UNCOV
919
    std::vector<XFSClassV2<PtrType, false>> layouts;
×
920
    rd.ReadContainer(layouts, header.numLayouts);
921
    std::transform(std::make_move_iterator(layouts.begin()),
×
922
                   std::make_move_iterator(layouts.end()),
×
UNCOV
923
                   std::back_inserter(main.rtti),
×
924
                   [](auto &&item) { return std::move(item); });
925

926
  } else if (memberSize == singleMemberSizePSN) {
UNCOV
927
    std::vector<XFSClassV2<PtrType, true>> layouts;
×
UNCOV
928
    rd.ReadContainer(layouts, header.numLayouts);
×
929
    std::transform(std::make_move_iterator(layouts.begin()),
×
930
                   std::make_move_iterator(layouts.end()),
×
931
                   std::back_inserter(main.rtti),
932
                   [](auto &&item) { return std::move(item); });
933
  } else {
UNCOV
934
    es::RuntimeError("Cannot detect member padding");
×
935
  }
UNCOV
936

×
937
  rd.Seek(header.dataStart);
×
938
}
×
939

940
bool LoadV2(XFSImpl &main, BinReaderRef_e rd) {
941
  XFSHeaderV2 header;
UNCOV
942
  rd.Read(header);
×
943
  rd.SetRelativeOrigin(rd.Tell(), false);
×
944
  uint32 offset;
945
  rd.Read(offset);
946
  rd.Seek(0);
947
  const size_t expectedEndX64 = header.numLayouts * 8;
UNCOV
948

×
UNCOV
949
  if (offset == expectedEndX64) {
×
950
    LoadV2<uint64>(main, rd, header);
951
    return true;
×
952
  }
×
953

×
954
  LoadV2<uint32>(main, rd, header);
×
955
  return false;
956
}
957

958
void XFSImpl::Load(BinReaderRef_e rd, bool openEnded) {
×
959
  using pt = Platform;
960
  XFSHeaderBase hdr;
×
961
  rd.Push();
×
962
  rd.Read(hdr);
×
963
  rd.Pop();
964

965
  if (hdr.id == XFSIDBE) {
966
    rd.SwapEndian(true);
×
967
    hdr.SwapEndian();
×
968
  } else if (hdr.id != XFSID) {
969
    throw es::InvalidHeaderError(hdr.id);
970
  }
971

972
  pt platform = rd.SwappedEndian() ? pt::PS3 : pt::Win32;
×
973
  bool isX64 = false;
974

975
  if (hdr.versionMajor == 0xf || hdr.versionMajor == 0x10) {
×
976
    isX64 = ::LoadV2(*this, rd);
977
  } else {
×
978
    if (platform == pt::Win32) {
×
979
      ::Load<uint32>(*this, rd);
980
    } else if (platform == pt::PS3) {
UNCOV
981
      ::Load<uint64>(*this, rd);
×
982
    } else {
×
983
      throw es::RuntimeError("Undefined platform!");
984
    }
985
  }
×
986

UNCOV
987
  for (auto &c : rtti) {
×
988
    c.className = GetClassName(c.hash);
UNCOV
989
#ifdef XFS_DEBUG
×
990
    if (c.className.empty() && !rttiStore.count(c.hash)) {
×
991
      rttiStore[c.hash] = c;
992
    }
993
#endif
994
  }
UNCOV
995

×
996
  if (isX64) {
UNCOV
997
    ReadData<uint64>(rd);
×
UNCOV
998
  } else {
×
999
    ReadData<uint32>(rd);
×
1000
  }
1001
  root = &dataStore.back();
×
1002

UNCOV
1003
  const size_t eof = rd.GetSize();
×
UNCOV
1004

×
1005
  if (!openEnded && eof != rd.Tell()) {
1006
    throw es::RuntimeError("Unexpected eof");
×
UNCOV
1007
  }
×
1008
}
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

© 2026 Coveralls, Inc