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

PredatorCZ / RevilLib / 119

25 Nov 2023 09:54AM UTC coverage: 12.368% (-0.03%) from 12.394%
119

push

github

PredatorCZ
fix

2 of 2 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

820 of 6630 relevant lines covered (12.37%)

6809.29 hits per line

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

90.44
/database/make_db.cpp
1
#include "database.hpp"
2
#include "revil/hashreg.hpp"
3
#include "spike/io/binwritter.hpp"
4
#include "spike/io/directory_scanner.hpp"
5
#include "spike/reflect/reflector.hpp"
6
#include <algorithm>
7
#include <array>
8
#include <functional>
9
#include <set>
10
#include <sstream>
11

12
MAKE_ENUM(ENUMSCOPE(class revil__Family, revil__Family), EMEMBER(NONE),
2✔
13
          EMEMBER(Classes), EMEMBER(ARC), EMEMBER(MOD), EMEMBER(LMT),
14
          EMEMBER(TEX), EMEMBER(XFS));
15

16
MAKE_ENUM(ENUMSCOPE(class revil__Platform, revil__Platform), EMEMBER(Auto),
2✔
17
          EMEMBER(Win32), EMEMBER(PS3), EMEMBER(X360), EMEMBER(N3DS),
18
          EMEMBER(CAFE), EMEMBER(NSW), EMEMBER(PS4), EMEMBER(Android),
19
          EMEMBER(IOS), EMEMBER(Win64));
20

21
using namespace revil;
22

23
using Executor = void (*)(revil__Platform, std::string_view, std::string_view);
24

25
struct DbClass {
9,998✔
26
  std::string name;
27
  std::string extension;
28
};
29

30
struct ArcSupport {
8✔
31
  uint8 version = 7;
32
  uint8 windowSize = 15;
33
  bool allowRaw = false;
34
  bool xmemOnly = false;
35
  bool extendedFilePath = false;
36
  std::string key{};
37
};
38

39
static constexpr ArcSupport ARC_PS3_GENERIC{8, 14, true};
40
static constexpr ArcSupport ARC_WINPC_GENERIC{};
41
static constexpr ArcSupport ARC_N3DS_GENERIC{0x11};
42

43
struct TitleDB {
44
  bool version1 = false;
45
  std::vector<std::string> titles;
46
  std::vector<DbClass> classes[NUM_PLATFORMS];
47
  std::vector<std::string> otherClasses;
48
  ArcSupport arcSuport[NUM_PLATFORMS]{
49
      {0},
50
      ARC_WINPC_GENERIC,
51
      ARC_PS3_GENERIC,
52
      {},
53
      ARC_N3DS_GENERIC,
54
      {},
55
      {},
56
      {},
57
      {},
58
      {},
59
      {},
60
  };
61
  uint16 modVersions[NUM_PLATFORMS]{};
62
  uint16 lmtVersions[NUM_PLATFORMS]{};
63
  uint16 texVersions[NUM_PLATFORMS]{};
64
  uint16 xfsVersions[NUM_PLATFORMS]{};
65
  bool usedPlatforms[NUM_PLATFORMS]{};
66
};
67

68
static std::vector<TitleDB> GLOBAL_DB;
69

70
uint32 GetNumber(std::string_view value) {
112✔
71
  return strtoul(value.data(), nullptr, 10 + 6 * value.starts_with("0x"));
112✔
72
}
73

74
static const Executor executors[]{
75
    [](revil__Platform, std::string_view key, std::string_view value) {
33✔
76
      if (key == "Version") {
29✔
77
        GLOBAL_DB.back().version1 = GetNumber(value) == 1;
4✔
78
      } else if (key == "Titles") {
29✔
79
        while (value.size() > 0) {
67✔
80
          const size_t found = value.find(',');
67✔
81

82
          GLOBAL_DB.back().titles.emplace_back(
134✔
83
              es::TrimWhitespace(value.substr(0, found)));
67✔
84

85
          if (found == value.npos) {
67✔
86
            break;
87
          }
88

89
          value.remove_prefix(found + 1);
38✔
90
          value = es::TrimWhitespace(value);
38✔
91
        }
92
      }
93
    },
33✔
94
    [](revil__Platform plt, std::string_view key, std::string_view value) {
3,204✔
95
      if (value.empty()) {
3,204✔
96
        GLOBAL_DB.back().otherClasses.emplace_back(key);
283✔
97
        return;
283✔
98
      }
99

100
      DbClass cls;
101
      cls.name = key;
102
      cls.extension = value;
103
      GLOBAL_DB.back().classes[uint32(plt)].emplace_back(cls);
2,921✔
104
    },
105
    [](revil__Platform plt, std::string_view key, std::string_view value) {
12✔
106
      auto &arc = GLOBAL_DB.back().arcSuport[uint32(plt)];
12✔
107
      if (key == "Version") {
4✔
108
        arc.version = GetNumber(value);
8✔
109
      } else if (key == "XMemdecompress") {
3✔
110
        arc.xmemOnly = true;
1✔
111
      } else if (key == "AllowRaw") {
3✔
112
        arc.allowRaw = true;
×
113
      } else if (key == "ExtendedPath") {
2✔
114
        arc.extendedFilePath = true;
1✔
115
      } else if (key == "WindowSize") {
2✔
116
        arc.windowSize = GetNumber(value);
×
117
      } else if (key == "Key") {
×
118
        arc.key = value;
2✔
119
      }
120
    },
12✔
121
    [](revil__Platform plt, std::string_view key, std::string_view value) {
34✔
122
      auto &mod = GLOBAL_DB.back().modVersions[uint32(plt)];
34✔
123
      if (key == "Version") {
×
124
        mod = GetNumber(value);
34✔
125
      }
126
    },
34✔
127
    [](revil__Platform plt, std::string_view key, std::string_view value) {
30✔
128
      auto &lmt = GLOBAL_DB.back().lmtVersions[uint32(plt)];
30✔
129
      if (key == "Version") {
×
130
        lmt = GetNumber(value);
30✔
131
      }
132
    },
30✔
133
    [](revil__Platform plt, std::string_view key, std::string_view value) {
36✔
134
      auto &tex = GLOBAL_DB.back().texVersions[uint32(plt)];
36✔
135
      if (key == "Version") {
×
136
        tex = GetNumber(value);
36✔
137
      }
138
    },
36✔
139
    [](revil__Platform plt, std::string_view key, std::string_view value) {
×
140
      auto &xfs = GLOBAL_DB.back().xfsVersions[uint32(plt)];
×
141
      if (key == "Version") {
×
142
        xfs = GetNumber(value);
×
143
      }
144
    },
×
145
};
146

147
struct StringSlider {
1✔
148
  std::string buffer;
149

150
  std::string::iterator FindString(std::string_view str) {
3,539✔
151
    if (str.size() > buffer.size()) [[unlikely]] {
3,539✔
152
      if (std::string_view(str).starts_with(buffer)) [[unlikely]] {
1✔
153
        buffer = str;
1✔
154
        return buffer.begin();
155
      } else {
156
        return buffer.end();
157
      }
158
    }
159
    auto searcher = std::boyer_moore_horspool_searcher(str.begin(), str.end());
3,538✔
160
    return std::search(buffer.begin(), buffer.end(), searcher);
3,538✔
161
  }
162

163
  size_t InsertString(std::string_view str) {
3,539✔
164
    auto found = FindString(str);
3,539✔
165

166
    if (found == buffer.end()) {
3,539✔
167
      buffer.append(str.data(), str.size());
2,849✔
168
      return buffer.size() - str.size();
2,849✔
169
    } else {
170
      return std::distance(buffer.begin(), found);
690✔
171
    }
172
  }
173
};
174

175
void LoadDb() {
1✔
176
  DirectoryScanner sc;
1✔
177
  sc.Scan("database/data");
2✔
178

179
  for (auto &f : sc) {
30✔
180
    std::ifstream stream(f);
29✔
181
    char buffer[0x100];
182
    size_t lineNum = 0;
183
    revil__Family curFamily = revil__Family::NONE;
184
    revil__Platform curPlatform = revil__Platform::Auto;
185
    GLOBAL_DB.emplace_back();
29✔
186
    bool usedArcs[NUM_PLATFORMS]{};
29✔
187

188
    while (!stream.eof()) {
3,601✔
189
      stream.getline(buffer, sizeof(buffer));
3,572✔
190
      lineNum++;
3,572✔
191
      std::string_view sv(buffer);
192
      es::TrimWhitespace(sv);
3,572✔
193

194
      if (sv.empty()) {
3,572✔
195
        continue;
223✔
196
      }
197

198
      if (sv.front() == '[') {
3,510✔
199
        if (sv.back() != ']') {
161✔
200
          throw std::runtime_error(f + ":" + std::to_string(lineNum) +
×
201
                                   ": expedted ] at the end");
×
202
        }
203

204
        sv.remove_prefix(1);
205
        sv.remove_suffix(1);
206

207
        const size_t foundSep = sv.find(':');
161✔
208
        std::string_view familyName(sv.substr(0, foundSep));
161✔
209
        static const auto familyRef = GetReflectedEnum<revil__Family>();
161✔
210
        bool foundFamily = false;
211

212
        for (size_t e = 0; e < familyRef->numMembers; e++) {
635✔
213
          if (familyRef->names[e] == familyName) {
837✔
214
            curFamily = static_cast<revil__Family>(e);
161✔
215
            curPlatform = revil__Platform::Auto;
216
            foundFamily = true;
217
            break;
161✔
218
          }
219
        }
220

221
        if (!foundFamily) {
161✔
222
          throw std::runtime_error(
×
223
              f + ":" + std::to_string(lineNum) +
×
224
              ": cannot find family: " + std::string(familyName));
×
225
        }
226

227
        if (foundSep != sv.npos) {
161✔
228
          std::string_view platform(sv.substr(foundSep + 1));
72✔
229

230
          if (platform.empty()) {
72✔
231
            throw std::runtime_error(f + ":" + std::to_string(lineNum) +
×
232
                                     ": expected platform name after :");
×
233
          }
234

235
          static const auto refPlatform = GetReflectedEnum<revil__Platform>();
72✔
236
          bool foundPlatform = false;
237

238
          for (size_t e = 0; e < refPlatform->numMembers; e++) {
321✔
239
            if (refPlatform->names[e] == platform) {
380✔
240
              curPlatform = static_cast<revil__Platform>(e);
72✔
241
              GLOBAL_DB.back().usedPlatforms[e] = true;
72✔
242
              foundPlatform = true;
243
              break;
72✔
244
            }
245
          }
246

247
          if (!foundPlatform) {
72✔
248
            throw std::runtime_error(
×
249
                f + ":" + std::to_string(lineNum) +
×
250
                ": cannot find platform: " + std::string(platform));
×
251
          }
252
        }
253

254
        continue;
255
      } else if (sv.back() == ']') {
3,510✔
256
        throw std::runtime_error(f + ":" + std::to_string(lineNum) +
×
257
                                 ": expedted [ at the start");
×
258
      }
259

260
      const size_t foundEq = sv.find('=');
3,349✔
261

262
      std::string_view key(es::TrimWhitespace(sv.substr(0, foundEq)));
3,349✔
263
      std::string_view value;
3,349✔
264

265
      if (foundEq != sv.npos) {
3,349✔
266
        value = es::TrimWhitespace(sv.substr(foundEq + 1));
3,064✔
267

268
        if (value.empty()) {
3,064✔
269
          throw std::runtime_error(f + ":" + std::to_string(lineNum) +
×
270
                                   ": expedted value after =");
×
271
        }
272
      }
273

274
      executors[uint32(curFamily)](curPlatform, key, value);
3,349✔
275

276
      if (curFamily == revil__Family::ARC) {
3,349✔
277
        usedArcs[uint32(curPlatform)] = true;
12✔
278
      }
279
    }
280

281
    if (usedArcs[0]) {
29✔
282
      for (uint32 p = 1; p < NUM_PLATFORMS; p++) {
77✔
283
        if (!usedArcs[p] && GLOBAL_DB.back().usedPlatforms[p]) {
70✔
284
          GLOBAL_DB.back().arcSuport[p] = GLOBAL_DB.back().arcSuport[0];
285
        }
286
      }
287
    }
288
  }
29✔
289
}
1✔
290

291
using CompareString = decltype([](auto &i1, auto &i2) {
115,065✔
292
  if (i1.size() == i2.size()) {
115,065✔
293
    return i1 < i2;
87,538✔
294
  }
295

296
  return i1.size() > i2.size();
27,527✔
297
});
298

112,191✔
299
auto DoStrings(BinWritterRef wr) {
112,191✔
300
  std::map<std::string_view, uint32> sliderOffsets;
84,742✔
301
  std::set<std::string_view, CompareString> strings;
302

303
  for (auto &d : GLOBAL_DB) {
27,449✔
304
    for (auto &t : d.titles) {
305
      strings.emplace(t);
2,874✔
306
    }
2,874✔
307

2,796✔
308
    for (auto &c : d.classes) {
309
      for (auto &i : c) {
310
        strings.emplace(i.name);
78✔
311

312
        if (i.extension.size() > 0) {
313
          strings.emplace(i.extension);
1✔
314
        }
315
      }
316
    }
317

30✔
318
    for (auto &c : d.otherClasses) {
96✔
319
      strings.emplace(c);
320
    }
321
  }
322

348✔
323
  StringSlider slider;
3,240✔
324
  const uint32 stringsBegin = wr.Tell();
2,921✔
325

326
  for (auto s : strings) {
2,921✔
327
    sliderOffsets.emplace(s, slider.InsertString(s) + stringsBegin);
2,921✔
328
  }
329

330
  for (auto &d : GLOBAL_DB) {
331
    for (uint32 p = 0; p < NUM_PLATFORMS; p++) {
332
      auto &key = d.arcSuport[p].key;
312✔
333
      if (key.size() > 0) {
334
        sliderOffsets.emplace(key, slider.InsertString(key) + stringsBegin);
335
      }
336
    }
337
  }
338

339
  wr.WriteContainer(slider.buffer);
340
  return sliderOffsets;
3,536✔
341
}
3,535✔
342

343
Header DoClasses(BinWritterRef wr,
344
                 const std::map<std::string_view, uint32> &sliderOffsets) {
30✔
345
  struct ExtInfo {
348✔
346
    std::map<uint32, uint32> platformWeights;
319✔
347
  };
319✔
348

4✔
349
  struct ClassInfo {
350
    std::map<std::string_view, ExtInfo> extensions;
351
    bool version21[2]{};
352
  };
353

354
  struct UClassInfo {
1✔
355
    bool version21[2]{};
356
  };
357

1✔
358
  std::map<std::string_view, ClassInfo> sortedClasses;
359
  std::map<std::string_view, UClassInfo> sortedUClasses;
360
  std::map<std::string_view, std::set<std::string_view>, CompareString>
361
      sortedExtensions;
362

363
  for (auto &d : GLOBAL_DB) {
1,739✔
364
    uint32 curPlatform = 0;
365
    for (auto &c : d.classes) {
366
      for (auto &i : c) {
367
        sortedClasses[i.name].version21[d.version1] = true;
368

275✔
369
        if (i.extension.size() > 0) {
370
          sortedClasses[i.name]
371
              .extensions[i.extension]
372
              .platformWeights[curPlatform]++;
373

374
          sortedExtensions[i.extension].emplace(i.name);
375
        }
376
      }
377

30✔
378
      curPlatform++;
29✔
379
    }
348✔
380

3,240✔
381
    for (auto &c : d.otherClasses) {
2,921✔
382
      sortedUClasses[c].version21[d.version1] = true;
383
    }
2,921✔
384
  }
2,921✔
385

2,921✔
386
  wr.ApplyPadding(4);
2,921✔
387
  std::vector<ResourceClass> rClasses;
388

2,921✔
389
  auto NewResClass = [&](auto &c, bool v1Hash) {
390
    auto &res = rClasses.emplace_back();
391

392
    if (v1Hash) {
319✔
393
      res.hash = MTHashV1(c.first);
394
    } else {
395
      res.hash = MTHashV2(c.first);
312✔
396
    }
283✔
397

398
    res.name.offset = sliderOffsets.at(c.first);
399
    res.name.size = c.first.size();
400

1✔
401
    if (c.second.extensions.size() == 1) {
1✔
402
      res.extension.offset =
403
          sliderOffsets.at(c.second.extensions.begin()->first);
1,803✔
404
      res.extension.size = c.second.extensions.begin()->first.size();
1,803✔
405
    } else {
406
      std::string_view mostAuto;
1,803✔
407
      size_t maxWeightAuto = 0;
125✔
408
      std::string_view mostOther;
409
      size_t maxWeightOther = 0;
1,678✔
410

411
      for (auto &[ex, plt] : c.second.extensions) {
412
        for (auto &[p, w] : plt.platformWeights) {
1,803✔
413
          if (p == 0) {
1,803✔
414
            if (w > maxWeightAuto) {
415
              maxWeightAuto = w;
1,803✔
416
              mostAuto = ex;
1,760✔
417
            }
1,760✔
418
          } else if (w > maxWeightOther) {
1,760✔
419
            maxWeightOther = w;
420
            mostOther = ex;
43✔
421
          }
422
        }
43✔
423
      }
424

425
      if (maxWeightAuto > 0) {
138✔
426
        res.extension.offset = sliderOffsets.at(mostAuto);
273✔
427
        res.extension.size = mostAuto.size();
178✔
428
      } else {
54✔
429
        res.extension.offset = sliderOffsets.at(mostOther);
430
        res.extension.size = mostOther.size();
33✔
431
      }
432
    }
124✔
433
  };
434

54✔
435
  for (auto &c : sortedClasses) {
436
    if (c.second.extensions.size() > 0) {
437

438
      if (c.second.version21[0]) {
439
        NewResClass(c, false);
43✔
440
      }
33✔
441

33✔
442
      if (c.second.version21[1]) {
443
        NewResClass(c, true);
10✔
444
      }
10✔
445
    }
446
  }
447
  auto WriteClasses = [&] {
1✔
448
    std::sort(rClasses.begin(), rClasses.end(),
449
              [](auto &c1, auto &c2) { return c1.hash < c2.hash; });
1,740✔
450

1,739✔
451
    for (auto &c : rClasses) {
452
      const int32 namePtrOffset = offsetof(ResourceClass, name) + wr.Tell();
1,739✔
453
      c.name.offset -= namePtrOffset;
1,678✔
454
      const int32 extPtrOffset = offsetof(ResourceClass, extension) + wr.Tell();
455
      c.extension.offset -= extPtrOffset;
456

1,739✔
457
      wr.Write(c);
125✔
458
    }
459

460
    es::Dispose(rClasses);
461
  };
11✔
462

11✔
463
  Header hdr{};
23,061✔
464
  hdr.resourceClasses[0].data.varPtr = wr.Tell() - 8;
465
  hdr.resourceClasses[0].numItems = rClasses.size();
1,914✔
466
  WriteClasses();
1,903✔
467

1,903✔
468
  auto NewResClassPlt = [&](auto &c, bool v1Hash, uint32 platform) {
1,903✔
469
    std::string_view mostAuto;
1,903✔
470
    size_t maxWeightAuto = 0;
471
    std::string_view mostOther;
472
    size_t maxWeightOther = 0;
473

474
    for (auto &[ex, plt] : c.second.extensions) {
11✔
475
      for (auto &[p, w] : plt.platformWeights) {
12✔
476
        if (p == 0) {
477
          if (w > maxWeightAuto) {
1✔
478
            maxWeightAuto = w;
1✔
479
            mostAuto = ex;
1✔
480
          }
1✔
481
        } else if (w > maxWeightOther) {
482
          maxWeightOther = w;
430✔
483
          mostOther = ex;
484
        }
485
      }
486
    }
487

488
    std::string_view mostExt;
1,380✔
489
    size_t maxWeight = 0;
2,730✔
490

1,780✔
491
    for (auto &[ex, plt] : c.second.extensions) {
540✔
492
      for (auto &[p, w] : plt.platformWeights) {
493
        if (p == platform) {
330✔
494
          if (w > maxWeight) {
495
            maxWeight = w;
1,240✔
496
            mostExt = ex;
497
          }
540✔
498
        }
499
      }
500
    }
501

502
    if (mostExt.empty() || (mostAuto.empty() && mostExt == mostOther)) {
430✔
503
      return;
504
    }
505

1,380✔
506
    auto &res = rClasses.emplace_back();
2,730✔
507

1,780✔
508
    if (v1Hash) {
124✔
509
      res.hash = MTHashV1(c.first);
510
    } else {
115✔
511
      res.hash = MTHashV2(c.first);
512
    }
513

514
    res.name.offset = sliderOffsets.at(c.first);
515
    res.name.size = c.first.size();
516

460✔
517
    res.extension.offset = sliderOffsets.at(mostExt);
330✔
518
    res.extension.size = mostExt.size();
519
  };
520

100✔
521
  for (uint32 p = 1; p < NUM_PLATFORMS; p++) {
522
    for (auto &c : sortedClasses) {
100✔
523
      auto &exts = c.second.extensions;
41✔
524

525
      if (exts.size() > 1) {
59✔
526
        if (c.second.version21[0]) {
527
          NewResClassPlt(c, false, p);
528
        }
100✔
529

100✔
530
        if (c.second.version21[1]) {
531
          NewResClassPlt(c, true, p);
100✔
532
        }
100✔
533
      }
1✔
534
    }
535

11✔
536
    hdr.resourceClasses[p].data.varPtr = wr.Tell() - (8 * p) - 8;
17,400✔
537
    hdr.resourceClasses[p].numItems = rClasses.size();
538
    WriteClasses();
539
  }
17,390✔
540

310✔
541
  es::Dispose(rClasses);
290✔
542

543
  std::vector<Class> uClasses;
544

310✔
545
  for (auto &[name, info] : sortedUClasses) {
140✔
546
    if (info.version21[0]) {
547
      auto &res = uClasses.emplace_back();
548
      res.hash = MTHashV2(name);
549
      res.name = {{
550
          .size = uint32(name.size()),
10✔
551
          .offset = int32(sliderOffsets.at(name)),
10✔
552
      }};
10✔
553
    }
554

555
    if (info.version21[1]) {
1✔
556
      auto &res = uClasses.emplace_back();
557
      res.hash = MTHashV1(name);
1✔
558
      res.name = {{
559
          .size = uint32(name.size()),
276✔
560
          .offset = int32(sliderOffsets.at(name)),
275✔
561
      }};
263✔
562
    }
263✔
563
  }
526✔
564

565
  std::sort(uClasses.begin(), uClasses.end(),
263✔
566
            [](auto &c1, auto &c2) { return c1.hash < c2.hash; });
567

568
  hdr.classes.data.varPtr = wr.Tell() - offsetof(Header, classes.data);
569
  hdr.classes.numItems = uClasses.size();
275✔
570

18✔
571
  for (auto &c : uClasses) {
18✔
572
    const int32 namePtrOffset = offsetof(Class, name) + wr.Tell();
36✔
573
    c.name.offset -= namePtrOffset;
574
    wr.Write(c);
18✔
575
  }
576

577
  es::Dispose(uClasses);
578
  // std::map<std::string_view, uint8, CompareString> extNs;
579
  std::set<std::string_view, CompareString> extNs;
1✔
580

2,542✔
581
  for (uint32 v = 0; v < 2; v++) {
582
    hdr.extensions[v].data.varPtr =
1✔
583
        wr.Tell() -
1✔
584
        (offsetof(Header, extensions[0].data) + sizeof(Array<Extension>) * v);
585

282✔
586
    for (auto &[ext, cls] : sortedExtensions) {
281✔
587
      std::vector<std::string_view> classes;
281✔
588

589
      for (auto &c : cls) {
590
        auto &cInfo = sortedClasses.at(c);
591
        if (cInfo.version21[v]) {
1✔
592
          classes.emplace_back(c);
593
        }
594
      }
595

3✔
596
      if (classes.size() != 1) {
2✔
597
        continue;
2✔
598
      }
599

600
      hdr.extensions[v].numItems++;
2,914✔
601

2,912✔
602
      wr.Write(Extension{
603
          .extension{{
6,462✔
604
              .size = uint32(ext.size()),
3,550✔
605
              .offset = int32(sliderOffsets.at(ext) - wr.Tell()),
3,550✔
606
          }},
1,855✔
607
          .hash = v ? MTHashV1(classes.front()) : MTHashV2(classes.front()),
608
      });
609
    }
610

2,912✔
611
    hdr.extensions4[v].data.varPtr =
612
        wr.Tell() -
613
        (offsetof(Header, extensions4[0].data) + sizeof(Array<Extension>) * v);
614

1,325✔
615
    for (auto &[ext, cls] : sortedExtensions) {
616
      std::vector<std::string_view> classes;
2,650✔
617

618
      for (auto &c : cls) {
619
        auto &cInfo = sortedClasses.at(c);
1,325✔
620
        if (cInfo.version21[v]) {
621
          classes.emplace_back(c);
1,325✔
622
        }
623
      }
624

625
      if (classes.size() < 2) {
2✔
626
        continue;
2✔
627
      }
628

629
      if (classes.size() > 4) {
2,914✔
630
        if (v == 0) {
2,912✔
631
          extNs.emplace(ext);
632
        } else {
6,462✔
633
          throw std::runtime_error("ExtensionN not implemented for version 1");
3,550✔
634
        }
3,550✔
635

1,855✔
636
        // extNs[ext] |= 1 << v;
637
        continue;
638
      }
639

2,912✔
640
      hdr.extensions4[v].numItems++;
2,689✔
641

642
      Extension4 ext4{
643
          .extension{{
223✔
644
              .size = uint32(ext.size()),
5✔
645
              .offset = int32(sliderOffsets.at(ext) - wr.Tell()),
646
          }},
647
          .hashes{},
×
648
      };
649

650
      for (size_t c = 0; c < classes.size(); c++) {
651
        ext4.hashes[c] = v ? MTHashV1(classes.at(c)) : MTHashV2(classes.at(c));
5✔
652
      }
653

654
      wr.Write(ext4);
218✔
655
    }
656
  }
218✔
657

658
  std::vector<SmallArray<uint32>> hashesN;
659

218✔
660
  /*for (auto &[ext, versions] : extNs) {
661
    SmallArray<uint32> curItem{
662
        .size = 0,
218✔
663
        .offset = int32(wr.Tell()),
664
    };
702✔
665

484✔
666
    auto &cls = sortedExtensions.at(ext);
667

668
    for (auto &c : cls) {
669
      auto &cInfo = sortedClasses.at(c);
670
      for (uint32 v = 0; v < 2; v++) {
671
        if ((versions & (1 << v)) && cInfo.version21[v]) {
672
          wr.Write(v ? MTHashV1(c) : MTHashV2(c));
1✔
673
          curItem.size++;
674
        }
675
      }
676
    }
677

678
    hashesN.emplace_back(curItem);
679
  }*/
680

681
  for (auto ext : extNs) {
682
    SmallArray<uint32> curItem{
683
        .size = 0,
684
        .offset = int32(wr.Tell()),
685
    };
686

687
    auto &cls = sortedExtensions.at(ext);
688

689
    for (auto &c : cls) {
690
      wr.Write(MTHashV2(c));
691
      curItem.size++;
692
    }
693

694
    hashesN.emplace_back(curItem);
695
  }
6✔
696

5✔
697
  hdr.extensionsN.data.varPtr = wr.Tell() - offsetof(Header, extensionsN.data);
698
  hdr.extensionsN.numItems = hashesN.size();
5✔
699

5✔
700
  for (auto keys = extNs.begin(); auto &arr : hashesN) {
701
    ExtensionN ext{
5✔
702
        .extension{{
703
            .size = uint32(keys->size()),
51✔
704
            .offset = int32(sliderOffsets.at(*keys) - wr.Tell()),
46✔
705
        }},
46✔
706
        .hashes = arr,
707
    };
708
    ext.hashes.offset -= wr.Tell() + offsetof(ExtensionN, hashes);
5✔
709
    wr.Write(ext);
710

711
    keys++;
1✔
712
  }
1✔
713

714
  return hdr;
6✔
715
}
716

717
size_t DoSupport(BinWritterRef wr,
718
                 const std::map<std::string_view, uint32> &sliderOffsets) {
5✔
719
  std::map<TitleSupport, uint32> supportPalette;
720

721
  auto DbArc = [&sliderOffsets](auto &t, uint32 p) {
10✔
722
    DbArcSupport arc{
5✔
723
        .version = t.arcSuport[p].version,
724
        .windowSize = t.arcSuport[p].windowSize,
725
        .flags =
726
            uint8(t.arcSuport[p].allowRaw |
727
                  (int(t.arcSuport[p].extendedFilePath) << 1) |
728
                  (int(t.arcSuport[p].xmemOnly) << 2) | (int(t.version1) << 3)),
1✔
729
        .key = {},
730
    };
731

1✔
732
    if (auto &key = t.arcSuport[p].key; key.size() > 0) {
733
      arc.key = {{
734
          .size = uint8(key.size()),
735
          .offset = int32(sliderOffsets.at(key)),
43✔
736
      }};
41✔
737
    }
41✔
738

41✔
739
    return arc;
740
  };
41✔
741

41✔
742
  std::vector<std::array<int32, NUM_PLATFORMS - 1>> indicesPalette;
41✔
743

744
  for (auto &t : GLOBAL_DB) {
745
    std::array<int32, NUM_PLATFORMS - 1> indices;
746

41✔
747
    for (uint32 p = 1; p < NUM_PLATFORMS; p++) {
2✔
748
      if (!t.usedPlatforms[p]) {
749
        indices[p - 1] = -1;
2✔
750
        continue;
751
      }
752

753
      TitleSupport supp{
41✔
754
          .arc = t.arcSuport[p].version ? DbArc(t, p) : DbArc(t, 0),
1✔
755
          .modVersion = t.modVersions[p] ? t.modVersions[p] : t.modVersions[0],
756
          .lmtVersion = t.lmtVersions[p] ? t.lmtVersions[p] : t.lmtVersions[0],
1✔
757
          .texVersion = t.texVersions[p] ? t.texVersions[p] : t.texVersions[0],
758
          .xfsVersion = t.xfsVersions[p] ? t.xfsVersions[p] : t.xfsVersions[0],
30✔
759
      };
760

761
      if (auto found = supportPalette.find(supp);
319✔
762
          found == supportPalette.end()) {
290✔
763
        indices[p - 1] = supportPalette.size();
249✔
764
        supportPalette.emplace(supp, supportPalette.size());
249✔
765
      } else {
766
        indices[p - 1] = found->second;
767
      }
768
    }
41✔
769

41✔
770
    indicesPalette.emplace_back(indices);
37✔
771
  }
29✔
772

41✔
773
  const size_t supportPaletteBegin = wr.Tell();
148✔
774

775
  {
41✔
776
    std::vector<TitleSupport> sortedSupports;
777
    sortedSupports.resize(supportPalette.size());
32✔
778

32✔
779
    for (auto &[t, i] : supportPalette) {
780
      sortedSupports.at(i) = t;
9✔
781
    }
782

783
    for (auto &s : sortedSupports) {
784
      if (s.arc.key.size > 0) {
29✔
785
        s.arc.key.offset -= wr.Tell() + offsetof(TitleSupport, arc.key);
786
      }
787

788
      wr.Write(s);
789
    }
790
  }
1✔
791

1✔
792
  const size_t indicesBegin = wr.Tell();
793

33✔
794
  for (auto i : indicesPalette) {
32✔
795
    for (auto &t : i) {
796
      if (t < 0) {
797
        t = 0;
33✔
798
      } else {
32✔
799
        t = (supportPaletteBegin + t * sizeof(TitleSupport)) - wr.Tell();
2✔
800
      }
801
      wr.Write(t);
802
    }
803
  }
804

805
  return indicesBegin;
806
}
807

808
std::vector<size_t>
30✔
809
DoTitleFixups(BinWritterRef wr, size_t indicesBegin, const Header *hdr,
319✔
810
              const std::map<std::string_view, uint32> &sliderOffsets) {
290✔
811
  std::vector<size_t> titleOffsets;
249✔
812
  auto &aClasses = hdr->resourceClasses[0];
813
  int32 curTitle = -1;
41✔
814

815
  for (auto &d : GLOBAL_DB) {
816
    curTitle++;
817
    std::map<uint32, std::vector<ResourceClass>> fixups;
818

819
    for (uint32 p = 0; p < NUM_PLATFORMS; p++) {
1✔
820
      if (!d.usedPlatforms[p] && p > 0) {
821
        continue;
822
      }
823

1✔
824
      auto &sClasses = hdr->resourceClasses[p];
825

1✔
826
      for (auto &c : d.classes[p]) {
827
        const uint32 hash = d.version1 ? MTHashV1(c.name) : MTHashV2(c.name);
828

829
        auto found = std::lower_bound(sClasses.begin(), sClasses.end(), hash);
30✔
830

29✔
831
        if (found == sClasses.end() || found->hash != hash) {
832
          found = std::lower_bound(aClasses.begin(), aClasses.end(), hash);
833

348✔
834
          if (found == aClasses.end()) {
319✔
835
            throw std::runtime_error("Cannot find class");
249✔
836
          }
837
        }
838

70✔
839
        if (found->hash != hash) {
840
          throw std::runtime_error("Cannot find class");
2,991✔
841
        }
2,921✔
842

843
        if (std::string_view(found->extension) != c.extension) {
844
          fixups[p].emplace_back(ResourceClass{
845
              Class{
2,921✔
846
                  .hash = hash,
847
                  .name = {{
848
                      .size = uint32(c.name.size()),
1,757✔
849
                      .offset = int32(sliderOffsets.at(c.name)),
×
850
                  }},
851
              },
852
              {{
853
                  .size = uint32(c.extension.size()),
2,921✔
854
                  .offset = int32(sliderOffsets.at(c.extension)),
×
855
              }},
856
          });
857
        }
2,880✔
858
      }
41✔
859
    }
860

861
    std::vector<Fixup> platformFixups;
862

863
    for (auto &[plt, fix] : fixups) {
41✔
864
      std::sort(fix.begin(), fix.end(),
865
                [](auto &c1, auto &c2) { return c1.hash < c2.hash; });
866
      platformFixups.emplace_back(Fixup{
867
          .classes{.varPtr = int32(wr.Tell())},
868
          .platform = uint8(plt),
41✔
869
          .numItems = uint16(fix.size()),
870
      });
871

872
      for (auto &c : fix) {
873
        const int32 namePtrOffset = offsetof(ResourceClass, name) + wr.Tell();
874
        c.name.offset -= namePtrOffset;
875
        const int32 extPtrOffset =
29✔
876
            offsetof(ResourceClass, extension) + wr.Tell();
877
        c.extension.offset -= extPtrOffset;
42✔
878

13✔
879
        wr.Write(c);
67✔
880
      }
13✔
881
    }
882

13✔
883
    const int32 fixupsBegin = wr.Tell();
884

885
    for (auto &f : platformFixups) {
886
      f.classes.varPtr -= wr.Tell();
54✔
887
      wr.Write(f);
41✔
888
    }
41✔
889

890
    Title title{
41✔
891
        .support{.varPtr = int32(indicesBegin +
41✔
892
                                 curTitle * sizeof(int32[NUM_PLATFORMS - 1])) -
893
                           int32(wr.Tell() - offsetof(Title, support))},
894
        .fixups{.size = uint32(platformFixups.size()),
895
                .offset =
896
                    (platformFixups.size() > 0) *
897
                    int32(fixupsBegin - wr.Tell() - offsetof(Title, fixups))},
898
    };
899

42✔
900
    titleOffsets.emplace_back(wr.Tell());
13✔
901
    wr.Write(title);
902
  }
903

904
  return titleOffsets;
905
}
29✔
906

29✔
907
void ValidateDb(const Header *shdr) {
908
  for (auto &d : GLOBAL_DB) {
909
    for (auto &t : d.titles) {
910
      auto oKey = LowerBound(t, shdr->titles);
58✔
911

29✔
912
      if (oKey == shdr->titles.end() || std::string_view(oKey->name) != t) {
29✔
913
        throw std::runtime_error("Cannot find title");
914
      }
29✔
915

916
      const Title &title = oKey->data;
917

918
      auto supports = title.support.operator->();
1✔
919

920
      for (size_t p = 1; p < NUM_PLATFORMS; p++) {
921
        if (!d.usedPlatforms[p]) {
1✔
922
          if (supports[p - 1].operator->()) {
30✔
923
            throw std::runtime_error("Validation failed");
96✔
924
          }
67✔
925

926
          continue;
134✔
927
        }
×
928

929
        const TitleSupport &support = supports[p - 1];
930

931
        if (support.arc.version != d.arcSuport[p].version) {
932
          throw std::runtime_error("Validation failed");
933
        }
934

737✔
935
        if (support.arc.windowSize != d.arcSuport[p].windowSize) {
670✔
936
          throw std::runtime_error("Validation failed");
572✔
937
        }
×
938

939
        if (std::string_view(support.arc.key) != d.arcSuport[p].key) {
940
          throw std::runtime_error("Validation failed");
572✔
941
        }
942

943
        if (bool(support.arc.flags & DbArc_AllowRaw) !=
98✔
944
            d.arcSuport[p].allowRaw) {
945
          throw std::runtime_error("Validation failed");
98✔
946
        }
×
947

948
        if (bool(support.arc.flags & DbArc_ExtendedPath) !=
949
            d.arcSuport[p].extendedFilePath) {
98✔
950
          throw std::runtime_error("Validation failed");
×
951
        }
952

953
        if (bool(support.arc.flags & DbArc_XMemCompress) !=
98✔
954
            d.arcSuport[p].xmemOnly) {
×
955
          throw std::runtime_error("Validation failed");
956
        }
957

98✔
958
        if (bool(support.arc.flags & DbArc_Version1) != d.version1) {
98✔
959
          throw std::runtime_error("Validation failed");
×
960
        }
961

962
        if (support.lmtVersion !=
98✔
963
            (d.lmtVersions[p] ? d.lmtVersions[p] : d.lmtVersions[0])) {
98✔
964
          throw std::runtime_error("Validation failed");
×
965
        }
966

967
        if (support.modVersion !=
98✔
968
            (d.modVersions[p] ? d.modVersions[p] : d.modVersions[0])) {
98✔
969
          throw std::runtime_error("Validation failed");
×
970
        }
971

972
        if (support.texVersion !=
98✔
973
            (d.texVersions[p] ? d.texVersions[p] : d.texVersions[0])) {
×
974
          throw std::runtime_error("Validation failed");
975
        }
976

98✔
977
        if (support.xfsVersion !=
98✔
978
            (d.xfsVersions[p] ? d.xfsVersions[p] : d.xfsVersions[0])) {
×
979
          throw std::runtime_error("Validation failed");
980
        }
981
      }
98✔
982

98✔
983
      for (size_t p = 0; p < NUM_PLATFORMS; p++) {
×
984
        auto &rClasses = d.classes[p];
985

986
        for (auto &c : rClasses) {
98✔
987
          const uint32 hash = d.version1 ? MTHashV1(c.name) : MTHashV2(c.name);
98✔
988
          bool found = false;
×
989

990
          for (auto &f : title.fixups) {
991
            if (f.platform == p) {
98✔
992
              [&] {
98✔
UNCOV
993
                const ResourceClass *fClasses = f.classes.operator->();
×
994

995
                for (size_t i = 0; i < f.numItems; i++) {
996
                  if (fClasses[i].hash == hash) {
997
                    if (std::string_view(fClasses[i].extension) !=
804✔
998
                        c.extension) {
737✔
999
                      throw std::runtime_error("Validation error");
1000
                    }
8,451✔
1001
                    found = true;
7,714✔
1002
                    return;
7,714✔
1003
                  }
1004
                }
12,156✔
1005
                return;
4,442✔
1006
              }();
4,134✔
1007
            }
4,134✔
1008
          }
1009

23,494✔
1010
          if (found) {
19,478✔
1011
            continue;
1012
          }
118✔
1013

×
1014
          auto foundClass = LowerBound(hash, shdr->resourceClasses[p]);
1015

118✔
1016
          if (foundClass == shdr->resourceClasses[p].end() ||
118✔
1017
              foundClass->hash != hash) {
1018
            foundClass = LowerBound(hash, shdr->resourceClasses[0]);
1019

1020
            if (foundClass == shdr->resourceClasses[0].end()) {
4,134✔
1021
              throw std::runtime_error("Validation error");
1022
            }
1023
          }
1024

7,714✔
1025
          if (std::string_view(foundClass->extension) != c.extension) {
118✔
1026
            throw std::runtime_error("Validation error");
1027
          }
1028

7,596✔
1029
          [&] {
1030
            for (auto cf :
7,596✔
1031
                 ClassesFromExtension(*shdr, c.extension, d.version1)) {
7,592✔
1032
              if (cf == hash) {
1033
                return;
1034
              }
3,725✔
1035
            }
×
1036

1037
            throw std::runtime_error("Validation error");
1038
          }();
1039
        }
7,596✔
1040
      }
×
1041
    }
1042

1043
    for (auto &c : d.otherClasses) {
7,596✔
1044
      const uint32 hash = d.version1 ? MTHashV1(c) : MTHashV2(c);
7,596✔
1045
      auto found = LowerBound(hash, shdr->classes);
9,670✔
1046

9,670✔
1047
      if (found == shdr->classes.end() || found->hash != hash ||
7,596✔
1048
          std::string_view(found->name) != c) {
1049
        throw std::runtime_error("Validation error");
1050
      }
1051
    }
×
1052
  }
7,596✔
1053
}
1054

1055
int main() {
1056
  LoadDb();
1057

312✔
1058
  std::stringstream str;
283✔
1059
  BinWritterRef wr(str);
283✔
1060
  wr.Write(Header{});
1061

283✔
1062
  std::map<std::string_view, uint32> sliderOffsets = DoStrings(wr);
283✔
1063
  Header hdr = DoClasses(wr, sliderOffsets);
×
1064
  const size_t indicesBegin = DoSupport(wr, sliderOffsets);
1065

1066
  wr.Push();
1067
  wr.Seek(0);
1✔
1068
  wr.Write(hdr);
1069
  wr.Pop();
1✔
1070

1✔
1071
  auto titleOffsets = DoTitleFixups(
1072
      wr, indicesBegin,
1✔
1073
      reinterpret_cast<const Header *>(str.rdbuf()->view().data()),
1074
      sliderOffsets);
1✔
1075

1076
  std::map<std::string_view, size_t, CompareString> titles;
1✔
1077

1✔
1078
  size_t curTitle = 0;
1✔
1079
  for (auto &d : GLOBAL_DB) {
1080
    for (auto &t : d.titles) {
1081
      titles.emplace(t, titleOffsets.at(curTitle));
1082
    }
1083
    curTitle++;
1084
  }
1085

1086
  hdr.titles.numItems = titles.size();
1087
  hdr.titles.data.varPtr = wr.Tell() - offsetof(Header, titles);
1088

1✔
1089
  for (auto &[tit, off] : titles) {
1090
    TitleName res{
1091
        .name{{
1092
            .size = uint32(tit.size()),
1093
            .offset = int32(sliderOffsets.at(tit) - wr.Tell() -
30✔
1094
                            offsetof(TitleName, name)),
96✔
1095
        }},
1096
        .data{.varPtr = int32(off - wr.Tell() - offsetof(TitleName, data))},
1097
    };
29✔
1098

1099
    wr.Write(res);
1100
  }
1✔
1101

1✔
1102
  wr.Seek(0);
1103
  wr.Write(hdr);
68✔
1104

1105
  // BinWritter wrn("database/redb");
1106
  // wrn.WriteContainer(str.rdbuf()->view());
1107

67✔
1108
  const uint64 *oData =
1109
      reinterpret_cast<const uint64 *>(str.rdbuf()->view().data());
1110
  const size_t numItems = (str.rdbuf()->view().size() + 7) / 8;
134✔
1111
  std::ofstream ostr("database/redb.c");
134✔
1112

1113
  try {
1114
    ValidateDb(reinterpret_cast<const Header *>(str.rdbuf()->view().data()));
1115
  } catch (const std::exception &e) {
1116
    ostr << "#pragma error \"" << e.what() << "\"" << std::endl;
1117
    throw;
1118
  }
1119

1120
  // C23 uses embed macro, wait for compiler support
1121
  ostr << "#include <stdint.h>\nconst uint64_t REDB[] "
1122
          "__attribute__((section(\".redb\"))) = {"
1123
       << std::hex;
1124

1✔
1125
  for (size_t i = 0; i < numItems; i++) {
1✔
1126
    ostr << "0x" << oData[i] << ',';
1127
  }
1128

1✔
1129
  ostr << "};\n";
×
1130

×
1131
  return 0;
×
1132
}
×
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