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

PredatorCZ / RevilLib / 117

06 Nov 2023 06:12PM UTC coverage: 5.723% (-0.6%) from 6.277%
117

push

github

PredatorCZ
add mod xD3 x64

2 of 28 new or added lines in 3 files covered. (7.14%)

1200 existing lines in 3 files now uncovered.

351 of 6133 relevant lines covered (5.72%)

743.71 hits per line

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

0.3
/src/sdl.cpp
1
/*  Revil Format Library
2
    Copyright(C) 2021-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
#define ES_COPYABLE_POINTER
19

20
#include "revil/sdl.hpp"
21
#include "pugixml.hpp"
22
#include "revil/hashreg.hpp"
23
#include "spike/except.hpp"
24
#include "spike/io/binreader_stream.hpp"
25
#include "spike/io/binwritter_stream.hpp"
26
#include "spike/reflect/reflector.hpp"
27
#include "spike/type/bitfield.hpp"
28
#include "spike/type/pointer.hpp"
29
#include "spike/type/vectors.hpp"
30
#include "spike/util/endian.hpp"
31
#include <array>
32
#include <cassert>
33
#include <sstream>
34

35
using namespace revil;
36

37
struct XFSClassMember;
38

39
MAKE_ENUM(ENUMSCOPE(class SDLType1
1✔
40
                    : uint8, SDLType1),
41
          EMEMBERVAL(RootNode, 1),        //
42
          EMEMBER(ClassNode),             //
43
          EMEMBER(SpecialClassNode),      //
44
          EMEMBERVAL(ClassMemberNode, 5), //
45
          EMEMBER(Int32),                 //
46
          EMEMBER(Vector4),               //
47
          EMEMBER(Float),                 //
48
          EMEMBER(Bool),                  //
49
          EMEMBER(NodeIndex),             //
50
          EMEMBER(ResourceInstance),      //
51
          EMEMBERVAL(BitFlags, 13),       //
52
          // UNUSED BELOW
53
          EMEMBERVAL(String, 128), //
54
          EMEMBER(Unit),           //
55
          EMEMBER(Curve)           //
56

57
);
58

59
MAKE_ENUM(ENUMSCOPE(class SDLType2
1✔
60
                    : uint8, SDLType2),
61
          EMEMBERVAL(RootNode, 1),          //
62
          EMEMBER(ClassNode),               //
63
          EMEMBERVAL(ClassMemberNode, 5),   //
64
          EMEMBER(Int32),                   //
65
          EMEMBERVAL(Vector4, 8),           //
66
          EMEMBER(Float),                   //
67
          EMEMBERVAL(Bool, 11),             //
68
          EMEMBER(NodeIndex),               //
69
          EMEMBERVAL(ResourceInstance, 13), //
70
          EMEMBER(String),                  //
71
          EMEMBER(Unit),                    //
72
          EMEMBER(Curve),                   //
73
          // UNUSED BELOW
74
          EMEMBERVAL(BitFlags, 128) //
75
);
76

77
enum class UsageType : uint8 {
78
  None,
79
  ClassMember,
80
  NodeRef,
81
  Boolean,
82
  Integer = 6,
83
  Decimal = 12,
84
  String = 14,
85
  Color,
86
  Vector3 = 20,
87
  Quaternion = 22,
88
  Vector2 = 34,
89
  Curve = 57,
90
  ResourcePath = 128,
91
};
92

93
struct SDLFrame {
94
  using Frame = BitMemberDecl<0, 24>;
95
  using Flags = BitMemberDecl<1, 8>;
96
  using type = BitFieldType<uint32, Frame, Flags>;
97
  type data;
98

99
  const type *operator->() const { return &data; }
100
  type *operator->() { return &data; }
101
};
102

103
struct SDLHeaderBase {
104
  uint32 id;
105
  uint16 version;
106
  uint16 numTracks;
107
};
108

109
struct SDLEntryV1 {
110
  SDLType1 type;
111
  UsageType usageType;
112
  uint16 numFrames;
113
  uint32 parentOrSlot;
114
  es::PointerX86<char> name;
115
  uint32 hashOrArrayIndex;
116
  es::PointerX86<SDLFrame> frames;
117
  es::PointerX86<char> data;
118
};
119

120
struct SDLHeaderV1 : SDLHeaderBase {
121
  SDLFrame maxFrame;
122
  uint32 baseTrack;
123
  es::PointerX86<char> strings;
124
  SDLEntryV1 entries[];
125
};
126

127
struct SDLEntryV2_x64 {
128
  SDLType2 type;
129
  UsageType usageType;
130
  uint16 numFrames;
131
  uint32 parentOrSlot;
132
  es::PointerX64<char> name;
133
  uint32 hashOrArrayIndex;
134
  uint32 unk2;
135
  uint64 unk3;
136
  es::PointerX64<SDLFrame> frames;
137
  es::PointerX64<char> data;
138
};
139

140
struct SDLHeaderV2_x64 : SDLHeaderBase {
141
  uint32 unk0;
142
  SDLFrame maxFrame;
143
  uint32 baseTrack;
144
  es::PointerX64<char> strings;
145
  SDLEntryV2_x64 entries[];
146
};
147

148
struct SDLHeaderV2_x86 : SDLHeaderBase {
149
  uint32 unk0;
150
  SDLFrame maxFrame;
151
  uint32 baseTrack;
152
  es::PointerX86<char> strings;
153
  SDLEntryV1 entries[];
154
};
155

156
static_assert(sizeof(SDLHeaderV2_x64) == 32);
157
static_assert(sizeof(SDLHeaderV2_x86) == 24);
158

159
template <> void FByteswapper(SDLEntryV1 &item, bool) {
×
160
  FByteswapper(item.parentOrSlot);
×
UNCOV
161
  FByteswapper(item.numFrames);
×
UNCOV
162
  FByteswapper(item.name);
×
UNCOV
163
  FByteswapper(item.hashOrArrayIndex);
×
UNCOV
164
  FByteswapper(item.frames);
×
165
  FByteswapper(item.data);
×
166
}
167

UNCOV
168
template <> void FByteswapper(SDLEntryV2_x64 &, bool) {
×
169
  // Not implemented
170
}
171

UNCOV
172
template <> void FByteswapper(SDLHeaderBase &item, bool) {
×
UNCOV
173
  FByteswapper(item.numTracks);
×
UNCOV
174
  FByteswapper(item.version);
×
175
}
176

UNCOV
177
template <> void FByteswapper(SDLHeaderV1 &item, bool way) {
×
UNCOV
178
  FByteswapper(static_cast<SDLHeaderBase &>(item));
×
UNCOV
179
  FByteswapper(item.baseTrack);
×
UNCOV
180
  FByteswapper(item.maxFrame.data, way);
×
UNCOV
181
  FByteswapper(item.strings);
×
182
}
183

UNCOV
184
template <> void FByteswapper(SDLHeaderV2_x86 &item, bool way) {
×
185
  FByteswapper(static_cast<SDLHeaderBase &>(item));
×
186
  FByteswapper(item.baseTrack);
×
UNCOV
187
  FByteswapper(item.maxFrame.data, way);
×
UNCOV
188
  FByteswapper(item.strings);
×
UNCOV
189
  FByteswapper(item.unk0);
×
190
}
191

UNCOV
192
template <> void FByteswapper(SDLHeaderV2_x64 &, bool) {
×
193
  // Not implemented
194
}
195

UNCOV
196
template <class C> void SwapData(C &entry, bool way = false) {
×
197
  size_t numBlocks = 0;
198
  using EnumType = decltype(entry.type);
199

UNCOV
200
  switch (entry.type) {
×
UNCOV
201
  case EnumType::Float:
×
202
  case EnumType::Int32:
203
  case EnumType::NodeIndex:
204
  case EnumType::ResourceInstance:
205
  case EnumType::String:
206
  case EnumType::Unit:
207
  case EnumType::BitFlags:
208
    numBlocks = 1;
209
    break;
×
210

211
  case EnumType::Vector4:
×
212
    numBlocks = 4;
UNCOV
213
    break;
×
UNCOV
214
  case EnumType::Curve:
×
215
    numBlocks = 16;
UNCOV
216
    break;
×
217

218
  default:
219
    break;
220
  }
221

UNCOV
222
  const size_t numSwaps = numBlocks * entry.numFrames;
×
223
  char *dataRaw = entry.data;
224
  uint32 *data = reinterpret_cast<uint32 *>(dataRaw);
225

UNCOV
226
  for (size_t i = 0; i < numSwaps; i++) {
×
UNCOV
227
    FByteswapper(data[i]);
×
228
  }
229

230
  SDLFrame *frames = entry.frames;
231

UNCOV
232
  for (size_t i = 0; i < numSwaps; i++) {
×
UNCOV
233
    FByteswapper(frames[i].data, way);
×
234
  }
235
}
UNCOV
236

×
237
template <class C> C FromXMLAttr(pugi::xml_node node, const char *attrName) {
238
  auto attr = node.attribute(attrName);
239

UNCOV
240
  if (attr.empty()) {
×
UNCOV
241
    throw std::runtime_error("Cannot find attribute: " + std::string(attrName) +
×
242
                             " for node: " + node.name());
243
  }
244

245
  if constexpr (std::is_same_v<C, int32>) {
246
    return attr.as_int();
247
  } else if constexpr (std::is_same_v<C, uint32>) {
248
    return attr.as_uint();
UNCOV
249
  } else if constexpr (std::is_same_v<C, bool>) {
×
250
    return attr.as_bool();
UNCOV
251
  } else if constexpr (std::is_same_v<C, float>) {
×
252
    return attr.as_float();
UNCOV
253
  } else if constexpr (std::is_same_v<C, double>) {
×
UNCOV
254
    return attr.as_double();
×
255
  } else if constexpr (std::is_same_v<C, int64>) {
UNCOV
256
    return attr.as_llong();
×
257
  } else if constexpr (std::is_same_v<C, uint64>) {
258
    return attr.as_ullong();
259
  } else if constexpr (std::is_same_v<C, const char *>) {
260
    return attr.as_string();
261
  }
262
}
×
263

264
pugi::xml_node XMLChild(pugi::xml_node parentNode, const char *nodeName) {
265
  auto node = parentNode.child(nodeName);
266

×
267
  if (node.empty()) {
×
268
    throw std::runtime_error(
269
        "Cannot find child node: " + std::string(nodeName) +
270
        " for node: " + node.name());
271
  }
UNCOV
272

×
273
  return node;
×
274
}
275

UNCOV
276
void ToXML(SDLFrame &frame, pugi::xml_node node) {
×
277
  node.append_attribute("frame").set_value(frame->Get<SDLFrame::Frame>());
278
  node.append_attribute("frameFlags").set_value(frame->Get<SDLFrame::Flags>());
279
}
280

×
UNCOV
281
void FromXML(SDLFrame &frame, pugi::xml_node node) {
×
282
  frame->Set<SDLFrame::Frame>(FromXMLAttr<uint32>(node, "frame"));
283
  frame->Set<SDLFrame::Flags>(FromXMLAttr<uint32>(node, "frameFlags"));
284
}
285

286
struct PaddingRange {
287
  uint32 offset;
288
  uint32 size;
UNCOV
289
};
×
290

291
SDLType2 GetSDLTypeV2(pugi::xml_node node) {
×
292
  static const auto refEnum = GetReflectedEnum<SDLType2>();
293

×
UNCOV
294
  for (size_t i = 0; i < refEnum->numMembers; ++i) {
×
295
    if (std::string_view(refEnum->names[i]) == node.name()) {
UNCOV
296
      return SDLType2(refEnum->values[i]);
×
297
    }
298
  }
299

300
  throw std::runtime_error(std::string("Unknown node type: ") + node.name());
301

302
  return SDLType2::RootNode;
×
303
};
304

305
struct NodeRef {
UNCOV
306
  uint32 offset;
×
UNCOV
307
  std::vector<std::string_view> nodeNames;
×
308
};
309

310
struct StringPointer {
311
  uint32 offset;
UNCOV
312
  uint32 stringId;
×
UNCOV
313
};
×
314

315
struct DataBuilder {
316
  std::vector<PaddingRange> paddingRanges;
UNCOV
317
  std::vector<StringPointer> stringPointers;
×
UNCOV
318
  std::map<std::string, size_t> strings;
×
319
  std::stringstream sstr;
UNCOV
320
  BinWritterRef dataWr{sstr};
×
UNCOV
321
  std::vector<SDLEntryV2_x64> items;
×
322
  std::map<std::string_view, uint32> classNodes;
323
  std::vector<NodeRef> nodeRefs;
324
  bool firstFrame = true;
325

326
  void WriteValues(pugi::xml_node entries, size_t parentIndex) {
×
327
    auto SetString = [this](auto &ptr, auto str) {
328
      if (auto found = strings.find(str); found != strings.end()) {
×
329
        ptr = reinterpret_cast<char *>(found->second);
330
      } else {
×
331
        const size_t newId = strings.size();
UNCOV
332
        strings.emplace(str, newId);
×
333
        ptr = reinterpret_cast<char *>(newId);
334
      }
335
    };
336

337
    auto ResourceType = [](pugi::xml_node &node) -> uint32 {
338
      if (auto rType = node.attribute("resourceType"); !rType.empty()) {
339
        return MTHashV2(rType.as_string());
UNCOV
340
        // TODO validate class
×
341
      }
342

343
      auto hashText = FromXMLAttr<const char *>(node, "resourceHash");
×
344

×
345
      return strtoul(hashText, nullptr, 16);
346
    };
×
UNCOV
347

×
348
    auto DoValues = [&](pugi::xml_node &node, SDLEntryV2_x64 &entry) {
349
      auto SaveFrames = [&] {
350
        std::vector<SDLFrame> frames;
351

352
        for (auto frame : node.children("frame")) {
353
          ::FromXML(frames.emplace_back(), frame);
354
        }
355

356
        entry.numFrames = frames.size();
×
357
        size_t reqArea = frames.size() * sizeof(SDLFrame);
358

359
        for (auto it = paddingRanges.begin(); it != paddingRanges.end(); it++) {
360
          if (it->size >= reqArea) {
361
            dataWr.Push();
362
            dataWr.Seek(it->offset);
363
            entry.frames = reinterpret_cast<SDLFrame *>(dataWr.Tell());
364
            dataWr.WriteContainer(frames);
365
            dataWr.Pop();
366

367
            if (it->size == reqArea) {
368
              paddingRanges.erase(it);
369
            } else {
×
UNCOV
370
              it->offset += reqArea;
×
371
              it->size -= reqArea;
372
            }
×
373
            return;
×
374
          }
375
        }
376

377
        entry.frames = reinterpret_cast<SDLFrame *>(dataWr.Tell());
UNCOV
378
        dataWr.WriteContainer(frames);
×
379
      };
380

381
      auto WriteValues = [&](auto value, const char *attrName = "value") {
382
        using value_type = decltype(value);
383
        for (auto frame : node.children("frame")) {
384
          dataWr.Write(FromXMLAttr<value_type>(frame, attrName));
385
        }
386
      };
387

388
      if (!firstFrame) {
389
        SaveFrames();
390
      }
391

392
      const size_t padding = GetPadding(dataWr.Tell(), 16);
393

394
      if (padding > 0) {
395
        paddingRanges.emplace_back(PaddingRange{
×
UNCOV
396
            .offset = uint32(dataWr.Tell()),
×
397
            .size = uint32(padding),
UNCOV
398
        });
×
UNCOV
399
        dataWr.Skip(padding);
×
400
      }
401

402
      entry.data = reinterpret_cast<char *>(dataWr.Tell());
403

404
      switch (entry.type) {
405
      case SDLType2::Float:
406
        WriteValues(float());
407
        break;
408
      case SDLType2::Vector4:
409
        for (auto frame : node.children("frame")) {
UNCOV
410
          dataWr.Write(FromXMLAttr<float>(frame, "x"));
×
411
          dataWr.Write(FromXMLAttr<float>(frame, "y"));
412
          dataWr.Write(FromXMLAttr<float>(frame, "z"));
413
          dataWr.Write(FromXMLAttr<float>(frame, "w"));
414
        }
415
        break;
416
      case SDLType2::Int32:
417
      case SDLType2::Unit:
418
        WriteValues(int32());
419
        break;
420
      case SDLType2::NodeIndex: {
UNCOV
421
        auto &nodeRef = nodeRefs.emplace_back();
×
UNCOV
422
        nodeRef.offset = dataWr.Tell();
×
423

UNCOV
424
        for (auto frame : node.children("frame")) {
×
425
          dataWr.Write<uint32>(0);
×
426
          nodeRef.nodeNames.emplace_back(
427
              FromXMLAttr<const char *>(frame, "nodeName"));
428
        }
429
        break;
430
      }
431
      case SDLType2::Bool:
432
        WriteValues(bool());
433
        dataWr.ApplyPadding(4);
434
        break;
435
      case SDLType2::ResourceInstance:
436
        for (auto frame : node.children("frame")) {
437
          if (frame.attribute("path").empty()) {
438
            dataWr.Write<uintptr_t>(0);
439
            continue;
440
          }
441

442
          const uint32 cHash = ResourceType(frame);
443
          std::string wString(sizeof(cHash), '-');
444
          wString.append(FromXMLAttr<const char *>(frame, "path"));
×
445
          memcpy(wString.data(), &cHash, sizeof(cHash));
446
          const char *ptr;
447
          SetString(ptr, wString);
×
448
          stringPointers.emplace_back(StringPointer{
×
449
              .offset = uint32(dataWr.Tell()),
450
              .stringId = uint32(reinterpret_cast<uintptr_t>(ptr)),
×
451
          });
×
452
          dataWr.Write(ptr);
453
        }
454
        break;
455
      case SDLType2::String:
456
        for (auto frame : node.children("frame")) {
457
          const char *ptr;
458
          SetString(ptr, FromXMLAttr<const char *>(frame, "value"));
×
459
          stringPointers.emplace_back(StringPointer{
460
              .offset = uint32(dataWr.Tell()),
461
              .stringId = uint32(reinterpret_cast<uintptr_t>(ptr)),
462
          });
463
          dataWr.Write(ptr);
464
        }
465
        break;
466
      case SDLType2::Curve:
467
        for (auto frame : node.children("frame")) {
468
          for (size_t i = 0; i < 16; i++) {
469
            auto aName = "e" + std::to_string(i);
470
            dataWr.Write(FromXMLAttr<float>(frame, aName.c_str()));
471
          }
472
        }
473
        break;
474

×
475
      default:
×
476
        break;
477
      }
×
478

×
479
      if (firstFrame) {
×
480
        firstFrame = false;
×
481
        SaveFrames();
482
      }
UNCOV
483
    };
×
484

485
    for (auto c : entries.children()) {
486
      SDLEntryV2_x64 entry{};
×
UNCOV
487
      entry.type = GetSDLTypeV2(c);
×
UNCOV
488
      entry.usageType = UsageType(FromXMLAttr<uint32>(c, "type"));
×
489
      auto nodeName = FromXMLAttr<const char *>(c, "name");
490
      SetString(entry.name, nodeName);
UNCOV
491

×
UNCOV
492
      switch (entry.type) {
×
UNCOV
493
      case SDLType2::RootNode:
×
494
        classNodes.emplace(nodeName, items.size());
495
        items.emplace_back(entry);
496
        WriteValues(c, 0);
497
        break;
498

499
      case SDLType2::ClassNode:
500
        entry.hashOrArrayIndex = ResourceType(c);
501
        entry.parentOrSlot = FromXMLAttr<uint32>(c, "entrySlot");
×
502
        classNodes.emplace(nodeName, items.size());
503
        items.emplace_back(entry);
UNCOV
504
        WriteValues(c, items.size() - 1);
×
UNCOV
505
        break;
×
UNCOV
506

×
507
      case SDLType2::ClassMemberNode:
508
        entry.hashOrArrayIndex = FromXMLAttr<uint32>(c, "arrayIndex");
509
        entry.parentOrSlot = parentIndex;
510
        items.emplace_back(entry);
×
511
        WriteValues(c, items.size() - 1);
512
        break;
513

514
      default:
515
        entry.hashOrArrayIndex = FromXMLAttr<uint32>(c, "arrayIndex");
×
516
        entry.parentOrSlot = parentIndex;
517
        DoValues(c, entry);
518
        items.emplace_back(entry);
519
        break;
520
      }
521
    }
522
  }
523
  /*
524
    void WriteFrames(pugi::xml_node entries) {
525
      auto DoFrames = [&](pugi::xml_node &node) {
526
        std::vector<SDLFrame> frames;
527

528
        for (auto frame : node.children("frame")) {
529
          ::FromXML(frames.emplace_back(), frame);
530
        }
531

532
        const size_t reqArea = frames.size() * sizeof(SDLFrame);
533

534
        if (reqArea > 12) {
535
          const uint32 retOffset = dataWr.Tell();
536
          dataWr.WriteContainer(frames);
×
537
          return retOffset;
×
538
        }
×
UNCOV
539

×
540
        for (auto it = paddingRanges.begin(); it != paddingRanges.end(); it++) {
541
          if (it->size == reqArea) {
×
542
            dataWr.Push();
×
543
            dataWr.Seek(it->offset);
×
544
            dataWr.WriteContainer(frames);
545
            dataWr.Pop();
546
            const uint32 retOffset = it->offset;
×
UNCOV
547
            paddingRanges.erase(it);
×
UNCOV
548
            return retOffset;
×
549
          }
550
        }
×
551

552
        auto bestAreaIt = paddingRanges.begin();
×
553
        uint32 minSize = 16;
554

UNCOV
555
        for (auto it = bestAreaIt; it != paddingRanges.end(); it++) {
×
556
          if (it->size > reqArea) {
×
557
            if (it->size < minSize) {
×
558
              bestAreaIt = it;
559
              minSize = it->size;
×
560
            }
UNCOV
561
          }
×
562
        }
563

UNCOV
564
        if (minSize < 16) {
×
UNCOV
565
          dataWr.Push();
×
UNCOV
566
          dataWr.Seek(bestAreaIt->offset);
×
567
          dataWr.WriteContainer(frames);
UNCOV
568
          dataWr.Pop();
×
UNCOV
569
          const uint32 retOffset = bestAreaIt->offset;
×
UNCOV
570
          bestAreaIt->offset += reqArea;
×
571
          bestAreaIt->size -= reqArea;
572
          return retOffset;
573
        }
UNCOV
574

×
UNCOV
575
        const uint32 retOffset = dataWr.Tell();
×
UNCOV
576
        dataWr.WriteContainer(frames);
×
577
        return retOffset;
578
      };
579

UNCOV
580
      for (auto c : entries.children()) {
×
581
        SDLType type = GetSDLType(c);
UNCOV
582

×
583
        switch (type) {
584
        case SDLType::RootNode:
UNCOV
585
          break;
×
UNCOV
586
        case SDLType::ArrayEntry:
×
UNCOV
587
        case SDLType::ClassNode:
×
588
          WriteFrames(c);
UNCOV
589
          break;
×
UNCOV
590

×
591
        default:
592
          frameOffsetStack.emplace_back(DoFrames(c));
UNCOV
593
          break;
×
UNCOV
594
        }
×
595
      }
UNCOV
596
    }*/
×
UNCOV
597
};
×
598

UNCOV
599
constexpr uint32 SDL_ID = CompileFourCC("SDL");
×
UNCOV
600
constexpr uint32 SDL_ID_BE = CompileFourCC("\0LDS");
×
UNCOV
601

×
602
void revil::SDLFromXML(BinWritterRef wr, pugi::xml_node rootNode) {
603
  auto classNode = XMLChild(rootNode, "class");
UNCOV
604

×
UNCOV
605
  if (std::string_view("rScheduler") !=
×
606
      FromXMLAttr<const char *>(classNode, "type")) {
UNCOV
607
    throw std::runtime_error("Invalid class type, expected rScheduler");
×
UNCOV
608
  }
×
609

610
  SDLHeaderV2_x64 hdr{};
611
  hdr.id = SDL_ID;
612
  hdr.version = 0x16;
613
  hdr.unk0 = 0xE2316427;
UNCOV
614
  ::FromXML(hdr.maxFrame, XMLChild(classNode, "maxFrame"));
×
UNCOV
615
  auto entries = XMLChild(classNode, "entries");
×
616
  DataBuilder dataBuilder;
617
  dataBuilder.WriteValues(entries, 0);
UNCOV
618
  hdr.numTracks = dataBuilder.items.size();
×
619

UNCOV
620
  wr.Write(hdr);
×
UNCOV
621
  wr.WriteContainer(dataBuilder.items);
×
622
  const size_t dataOffset = wr.Tell();
623
  std::string dataBuffer(std::move(dataBuilder.sstr).str());
UNCOV
624
  wr.WriteContainer(dataBuffer);
×
625
  es::Dispose(dataBuffer);
UNCOV
626
  uintptr_t stringBegin = wr.Tell();
×
UNCOV
627
  hdr.strings = reinterpret_cast<char *>(stringBegin);
×
628
  std::vector<uintptr_t> stringOffsets;
629
  std::vector<std::string_view> strings;
UNCOV
630
  strings.resize(dataBuilder.strings.size());
×
631

UNCOV
632
  for (auto &[str, id] : dataBuilder.strings) {
×
UNCOV
633
    strings.at(id) = str.c_str();
×
634
  }
635

UNCOV
636
  for (size_t i = 0; i < strings.size(); i++) {
×
637
    stringOffsets.emplace_back(wr.Tell() - stringBegin);
UNCOV
638
    wr.WriteT(strings.at(i));
×
UNCOV
639
  }
×
640

641
  for (auto &e : dataBuilder.items) {
642
    e.name = reinterpret_cast<char *>(
643
        stringOffsets.at(reinterpret_cast<uintptr_t &>(e.name)));
×
644

×
645
    if (e.numFrames > 0) {
646
      e.data.FixupRelative(reinterpret_cast<char *>(dataOffset));
647
      e.frames.FixupRelative(reinterpret_cast<char *>(dataOffset));
648
    }
649
  }
UNCOV
650

×
651
  for (auto p : dataBuilder.stringPointers) {
652
    wr.Seek(dataOffset + p.offset);
653
    wr.Write(stringOffsets.at(p.stringId));
654
  }
×
655

656
  for (auto r : dataBuilder.nodeRefs) {
657
    wr.Seek(dataOffset + r.offset);
×
658

659
    for (auto &n : r.nodeNames) {
×
UNCOV
660
      wr.Write(dataBuilder.classNodes.at(n));
×
UNCOV
661
    }
×
662
  }
UNCOV
663

×
UNCOV
664
  wr.Seek(0);
×
UNCOV
665
  wr.Write(hdr);
×
UNCOV
666
  wr.WriteContainer(dataBuilder.items);
×
UNCOV
667
}
×
668

×
669
template <class HdrType> void ToXML(HdrType *hdr, pugi::xml_node root) {
670
  using EntryType = std::decay_t<decltype(hdr->entries[0])>;
×
671
  using EnumType = decltype(EntryType::type);
×
672
  using PtrTypeChar = decltype(hdr->strings);
673
  using PtrTypeUint =
×
674
      std::conditional_t<sizeof(PtrTypeChar) == 8, es::PointerX64<uint32>,
UNCOV
675
                         es::PointerX86<uint32>>;
×
UNCOV
676

×
677
  ::ToXML(hdr->maxFrame, root.append_child("maxFrame"));
×
678

679
  if (hdr->baseTrack > 0) {
×
UNCOV
680
    auto &entry = hdr->entries[hdr->baseTrack];
×
UNCOV
681
    std::string xmlTrack(
×
682
        static_cast<const char *>(hdr->entries[entry.parentOrSlot].name));
×
683
    xmlTrack.append("::");
684
    xmlTrack.append(static_cast<const char *>(entry.name));
×
685

686
    root.append_attribute("baseTrack").set_value(xmlTrack.c_str());
×
687
  }
×
UNCOV
688

×
689
  auto entries = root.append_child("entries");
UNCOV
690
  std::vector<pugi::xml_node> nodes;
×
UNCOV
691
  pugi::xml_node currentRoot;
×
692

×
693
  for (size_t i = 0; i < hdr->numTracks; i++) {
×
694
    auto &entry = hdr->entries[i];
×
695

696
    if constexpr (std::is_same_v<HdrType, SDLHeaderV2_x64>) {
697
      assert(entry.unk2 == 0);
×
698
      assert(entry.unk3 == 0);
UNCOV
699
    }
×
700
    static const auto refEnum = GetReflectedEnum<EnumType>();
701
    auto typeName = [&] {
UNCOV
702
      const size_t numEns = refEnum->numMembers;
×
UNCOV
703

×
704
      for (size_t i = 0; i < numEns; i++) {
705
        if (refEnum->values[i] == static_cast<uint64>(entry.type)) {
706
          return refEnum->names[i];
707
        }
708
      }
UNCOV
709

×
710
      return "__UNREGISTERED__";
×
UNCOV
711
    }();
×
712

UNCOV
713
    pugi::xml_node xEntry;
×
714

×
715
    auto SetClassName = [](pugi::xml_node &node, uint32 hash) {
716
      auto clName = GetClassName(hash, Platform::Win32);
717

718
      if (clName.empty()) {
719
        char buffer[0x10]{};
UNCOV
720
        snprintf(buffer, sizeof(buffer), "%X", hash);
×
UNCOV
721
        node.append_attribute("resourceHash").set_value(buffer);
×
722
      } else {
×
723
        node.append_attribute("resourceType").set_value(clName.data());
×
724
      }
×
UNCOV
725
    };
×
726

727
    switch (entry.type) {
UNCOV
728
    case EnumType::Float:
×
729
    case EnumType::Vector4:
730
    case EnumType::Int32:
731
    case EnumType::ClassMemberNode:
732
    case EnumType::Bool:
733
    case EnumType::NodeIndex:
734
    case EnumType::ResourceInstance:
×
735
    case EnumType::String:
×
736
    case EnumType::Unit:
×
737
    case EnumType::Curve:
738
    case EnumType::BitFlags:
739
      xEntry = nodes.at(entry.parentOrSlot).append_child(typeName);
UNCOV
740
      xEntry.append_attribute("arrayIndex").set_value(entry.hashOrArrayIndex);
×
UNCOV
741
      break;
×
UNCOV
742

×
743
    default:
×
UNCOV
744
      xEntry = currentRoot.append_child(typeName);
×
745
      xEntry.append_attribute("entrySlot").set_value(entry.parentOrSlot);
×
746
      SetClassName(xEntry, entry.hashOrArrayIndex);
UNCOV
747
      break;
×
748
    case EnumType::RootNode:
×
749
      xEntry = currentRoot = entries.append_child(typeName);
×
UNCOV
750
      assert(entry.parentOrSlot == 0);
×
751
      break;
×
752
    }
753

UNCOV
754
    nodes.emplace_back(xEntry);
×
UNCOV
755

×
UNCOV
756
    xEntry.append_attribute("name").set_value(
×
757
        static_cast<const char *>(entry.name));
×
758
    xEntry.append_attribute("type").set_value(uint8(entry.usageType));
×
UNCOV
759
    //xEntry.append_attribute("id").set_value(i);
×
760

761
    if (entry.numFrames > 0) {
UNCOV
762
      SDLFrame *frames = entry.frames;
×
UNCOV
763

×
UNCOV
764
      for (auto f = 0; f < entry.numFrames; f++) {
×
UNCOV
765
        auto frame = frames[f];
×
UNCOV
766
        auto xFrame = xEntry.append_child("frame");
×
767
        ::ToXML(frame, xFrame);
768

769
        switch (entry.type) {
×
770
        case EnumType::Int32:
×
UNCOV
771
        case EnumType::Unit:
×
772
          xFrame.append_attribute("value").set_value(
×
773
              reinterpret_cast<int32 *>(static_cast<char *>(entry.data))[f]);
×
774
          break;
775
        case EnumType::Vector4: {
776
          auto &value =
777
              reinterpret_cast<Vector4 *>(static_cast<char *>(entry.data))[f];
778
          xFrame.append_attribute("x").set_value(value.x);
779
          xFrame.append_attribute("y").set_value(value.y);
780
          xFrame.append_attribute("z").set_value(value.z);
781
          xFrame.append_attribute("w").set_value(value.w);
782
          break;
783
        }
784
        case EnumType::Float:
785
          xFrame.append_attribute("value").set_value(
786
              reinterpret_cast<float *>(static_cast<char *>(entry.data))[f]);
787
          break;
788
        case EnumType::Bool:
789
          xFrame.append_attribute("value").set_value(
790
              reinterpret_cast<bool *>(static_cast<char *>(entry.data))[f]);
791
          break;
792
        case EnumType::BitFlags:
793
          xFrame.append_attribute("value").set_value(
794
              reinterpret_cast<uint32 *>(static_cast<char *>(entry.data))[f]);
795
          break;
796
        case EnumType::NodeIndex:
797
          xFrame.append_attribute("nodeName")
798
              .set_value(static_cast<const char *>(
799
                  hdr->entries[reinterpret_cast<uint32 *>(
800
                                   static_cast<char *>(entry.data))[f]]
801
                      .name));
802
          break;
803

804
        case EnumType::ResourceInstance: {
805
          auto &dataPtr = reinterpret_cast<PtrTypeUint *>(
806
              static_cast<char *>(entry.data))[f];
807

808
          if (dataPtr) {
809
            SetClassName(xFrame, *dataPtr);
810
            xFrame.append_attribute("path").set_value(
811
                reinterpret_cast<const char *>(dataPtr.operator->() + 1));
812
          }
813

814
          break;
815
        }
816

817
        case EnumType::Curve: {
818
          auto &value = reinterpret_cast<std::array<float, 16> *>(
819
              static_cast<char *>(entry.data))[f];
820
          for (size_t i = 0; i < value.size(); i++) {
821
            auto aName = "e" + std::to_string(i);
822
            xFrame.append_attribute(aName.c_str()).set_value(value[i]);
823
          }
824
          break;
825
        }
826

827
        case EnumType::String:
828
          xFrame.append_attribute("value").set_value(
829
              static_cast<const char *>(reinterpret_cast<PtrTypeChar *>(
830
                  static_cast<char *>(entry.data))[f]));
831

832
          break;
833

834
        default:
835
          break;
836
        }
837
      }
838
    }
839
  }
840
}
841

842
class revil::SDLImpl {
843
public:
844
  std::string buffer;
845

846
  bool IsX86() const {
847
    auto hdr = reinterpret_cast<const SDLHeaderV2_x86 *>(buffer.data());
848
    // Member strings overlaps with padding after baseTrack
849
    // Big endian are always x86
850
    // There are no MTF V1 x64 schedulers
851

852
    return hdr->version < 0x10 || hdr->id == SDL_ID_BE || hdr->strings;
853
  }
854

855
  void ToXML(pugi::xml_node node) {
856
    auto root = node.append_child("class");
UNCOV
857
    root.append_attribute("type").set_value("rScheduler");
×
UNCOV
858
    auto hdrBase = reinterpret_cast<SDLHeaderBase *>(buffer.data());
×
859

860
    if (hdrBase->version < 0x10) {
UNCOV
861
      auto hdr = reinterpret_cast<SDLHeaderV1 *>(buffer.data());
×
UNCOV
862
      ::ToXML(hdr, root);
×
863
    } else {
864
      if (IsX86()) {
865
        auto hdr = reinterpret_cast<SDLHeaderV2_x86 *>(buffer.data());
×
UNCOV
866
        ::ToXML(hdr, root);
×
UNCOV
867
      } else {
×
868
        auto hdrx64 = reinterpret_cast<SDLHeaderV2_x64 *>(buffer.data());
×
869
        ::ToXML(hdrx64, root);
×
UNCOV
870
      }
×
UNCOV
871
    }
×
872
  }
×
UNCOV
873
  void Load(BinReaderRef_e rd) {
×
874
    uint32 id;
875
    rd.Read(id);
UNCOV
876
    rd.Seek(0);
×
877

878
    if (id == SDL_ID_BE) {
879
      rd.SwapEndian(true);
880
    } else if (id != SDL_ID) {
881
      throw es::InvalidHeaderError(id);
UNCOV
882
    }
×
883

×
UNCOV
884
    rd.ReadContainer(buffer, rd.GetSize());
×
885

×
886
    auto hdrBase = reinterpret_cast<SDLHeaderBase *>(buffer.data());
UNCOV
887

×
888
    if (hdrBase->version < 0x10) {
×
889
      auto hdr = reinterpret_cast<SDLHeaderV1 *>(buffer.data());
890
      hdr->strings.Fixup(buffer.data());
UNCOV
891

×
UNCOV
892
      for (size_t i = 0; i < hdr->numTracks; i++) {
×
UNCOV
893
        auto &entry = hdr->entries[i];
×
894
        es::FixupPointers(buffer.data(), entry.data, entry.frames);
895
        // This can be misleading, since null is allowed only for root nodes
896
        entry.name.FixupRelative(hdr->strings);
×
897

898
        if (entry.type == SDLType1::ResourceInstance ||
×
899
            entry.type == SDLType1::String) {
900
          for (auto f = 0; f < entry.numFrames; f++) {
×
UNCOV
901
            reinterpret_cast<es::PointerX86<char> *>(
×
902
                static_cast<char *>(entry.data))[f]
903
                .Fixup(hdr->strings);
904
          }
905
        }
UNCOV
906
      }
×
UNCOV
907
    } else {
×
UNCOV
908
      auto FixupStuff = [&](auto *hdr) {
×
909
        const bool shouldSwap = hdr->id == SDL_ID_BE;
910

UNCOV
911
        if (shouldSwap) {
×
UNCOV
912
          FByteswapper(*hdr);
×
913
        }
UNCOV
914

×
UNCOV
915
        hdr->strings.Fixup(buffer.data());
×
916

917
        for (size_t i = 0; i < hdr->numTracks; i++) {
918
          auto &entry = hdr->entries[i];
919
          if (shouldSwap) {
920
            FByteswapper(entry);
UNCOV
921
          }
×
922

923
          es::FixupPointers(buffer.data(), entry.data, entry.frames);
UNCOV
924
          // This can be misleading, since null is allowed only for root nodes
×
925
          entry.name.FixupRelative(hdr->strings);
926

927
          if (shouldSwap) {
928
            SwapData(entry);
929
          }
930

931
          using EnumType = decltype(entry.type);
UNCOV
932

×
933
          if (entry.type == EnumType::ResourceInstance ||
UNCOV
934
              entry.type == EnumType::String) {
×
935
            for (auto f = 0; f < entry.numFrames; f++) {
UNCOV
936
              reinterpret_cast<es::PointerX64<char> *>(
×
937
                  static_cast<char *>(entry.data))[f]
UNCOV
938
                  .Fixup(hdr->strings);
×
UNCOV
939
            }
×
940
          }
UNCOV
941
        }
×
942
      };
943

UNCOV
944
      if (IsX86()) {
×
UNCOV
945
        auto hdr = reinterpret_cast<SDLHeaderV2_x86 *>(buffer.data());
×
UNCOV
946
        FixupStuff(hdr);
×
947
      } else {
UNCOV
948
        auto hdr = reinterpret_cast<SDLHeaderV2_x64 *>(buffer.data());
×
UNCOV
949
        FixupStuff(hdr);
×
950
      }
951
    }
952
  }
953
};
954

955
SDL::SDL() : pi(std::make_unique<SDLImpl>()) {}
956
SDL::~SDL() = default;
UNCOV
957

×
958
void SDL::Load(BinReaderRef_e rd) { pi->Load(rd); }
UNCOV
959

×
UNCOV
960
void SDL::ToXML(pugi::xml_node node) const { pi->ToXML(node); }
×
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