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

PredatorCZ / RevilLib / 176

13 Apr 2026 04:45PM UTC coverage: 10.756% (+0.1%) from 10.614%
176

push

github

PredatorCZ
update database

770 of 7159 relevant lines covered (10.76%)

5900.01 hits per line

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

90.32
/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), EMEMBER(SDL));
15

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

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

24
struct DbClass {
10,401✔
25
  std::string name;
26
  std::string extension;
27
};
28

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

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

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

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

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

74
static const Executor executors[]{
75
    [](revil__Platform, std::string_view key, std::string_view value) {
32✔
76
      if (key == "Version") {
29✔
77
        GLOBAL_DB.back().version1 = GetNumber(value) == 1;
3✔
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
    },
32✔
94
    [](revil__Platform plt, std::string_view key, std::string_view value) {
3,642✔
95
      if (value.empty()) {
3,642✔
96
        GLOBAL_DB.back().otherClasses.emplace_back(key);
582✔
97
        return;
582✔
98
      }
99

100
      DbClass cls;
101
      cls.name = key;
102
      cls.extension = value;
103
      GLOBAL_DB.back().classes[uint32(plt)].emplace_back(cls);
3,060✔
104
    },
105
    [](revil__Platform plt, std::string_view key, std::string_view value) {
15✔
106
      auto &arc = GLOBAL_DB.back().arcSuport[uint32(plt)];
15✔
107
      if (key == "Version") {
5✔
108
        arc.version = GetNumber(value);
10✔
109
      } else if (key == "XMemdecompress") {
4✔
110
        arc.xmemOnly = true;
1✔
111
      } else if (key == "AllowRaw") {
4✔
112
        arc.allowRaw = true;
×
113
      } else if (key == "ExtendedPath") {
2✔
114
        arc.extendedFilePath = true;
2✔
115
      } else if (key == "WindowSize") {
2✔
116
        arc.windowSize = GetNumber(value);
×
117
      } else if (key == "Key") {
×
118
        arc.key = value;
2✔
119
      }
120
    },
15✔
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
    [](revil__Platform plt, std::string_view key, std::string_view value) {
4✔
146
      auto &sdl = GLOBAL_DB.back().sdlVersions[uint32(plt)];
4✔
147
      if (key == "Version") {
×
148
        sdl = GetNumber(value);
4✔
149
      }
150
    },
4✔
151
};
152

153
struct StringSlider {
1✔
154
  std::string buffer;
155

156
  std::string::iterator FindString(std::string_view str) {
3,923✔
157
    if (str.size() > buffer.size()) [[unlikely]] {
3,923✔
158
      if (std::string_view(str).starts_with(buffer)) [[unlikely]] {
1✔
159
        buffer = str;
1✔
160
        return buffer.begin();
161
      } else {
162
        return buffer.end();
163
      }
164
    }
165
    auto searcher = std::boyer_moore_horspool_searcher(str.begin(), str.end());
3,922✔
166
    return std::search(buffer.begin(), buffer.end(), searcher);
3,922✔
167
  }
168

169
  size_t InsertString(std::string_view str) {
3,923✔
170
    auto found = FindString(str);
3,923✔
171

172
    if (found == buffer.end()) {
3,923✔
173
      buffer.append(str.data(), str.size());
3,144✔
174
      return buffer.size() - str.size();
3,144✔
175
    } else {
176
      return std::distance(buffer.begin(), found);
779✔
177
    }
178
  }
179
};
180

181
void LoadDb() {
1✔
182
  DirectoryScanner sc;
1✔
183
  sc.Scan("database/data");
2✔
184

185
  for (auto &f : sc) {
30✔
186
    std::ifstream stream(f);
29✔
187
    char buffer[0x100];
188
    size_t lineNum = 0;
189
    revil__Family curFamily = revil__Family::NONE;
190
    revil__Platform curPlatform = revil__Platform::Auto;
191
    GLOBAL_DB.emplace_back();
29✔
192
    bool usedArcs[NUM_PLATFORMS]{};
29✔
193

194
    while (!stream.eof()) {
4,056✔
195
      stream.getline(buffer, sizeof(buffer));
4,027✔
196
      lineNum++;
4,027✔
197
      std::string_view sv(buffer);
198
      es::TrimWhitespace(sv);
4,027✔
199

200
      if (sv.empty()) {
4,027✔
201
        continue;
234✔
202
      }
203

204
      if (sv.front() == '[') {
3,965✔
205
        if (sv.back() != ']') {
172✔
206
          throw std::runtime_error(f + ":" + std::to_string(lineNum) +
×
207
                                   ": expedted ] at the end");
×
208
        }
209

210
        sv.remove_prefix(1);
211
        sv.remove_suffix(1);
212

213
        const size_t foundSep = sv.find(':');
172✔
214
        std::string_view familyName(sv.substr(0, foundSep));
172✔
215
        static const auto familyRef = GetReflectedEnum<revil__Family>();
172✔
216
        bool foundFamily = false;
217

218
        for (size_t e = 0; e < familyRef->numMembers; e++) {
683✔
219
          if (familyRef->names[e] == familyName) {
905✔
220
            curFamily = static_cast<revil__Family>(e);
172✔
221
            curPlatform = revil__Platform::Auto;
222
            foundFamily = true;
223
            break;
172✔
224
          }
225
        }
226

227
        if (!foundFamily) {
172✔
228
          throw std::runtime_error(
×
229
              f + ":" + std::to_string(lineNum) +
×
230
              ": cannot find family: " + std::string(familyName));
×
231
        }
232

233
        if (foundSep != sv.npos) {
172✔
234
          std::string_view platform(sv.substr(foundSep + 1));
75✔
235

236
          if (platform.empty()) {
75✔
237
            throw std::runtime_error(f + ":" + std::to_string(lineNum) +
×
238
                                     ": expected platform name after :");
×
239
          }
240

241
          static const auto refPlatform = GetReflectedEnum<revil__Platform>();
75✔
242
          bool foundPlatform = false;
243

244
          for (size_t e = 0; e < refPlatform->numMembers; e++) {
333✔
245
            if (refPlatform->names[e] == platform) {
381✔
246
              curPlatform = static_cast<revil__Platform>(e);
75✔
247
              GLOBAL_DB.back().usedPlatforms[e] = true;
75✔
248
              foundPlatform = true;
249
              break;
75✔
250
            }
251
          }
252

253
          if (!foundPlatform) {
75✔
254
            throw std::runtime_error(
×
255
                f + ":" + std::to_string(lineNum) +
×
256
                ": cannot find platform: " + std::string(platform));
×
257
          }
258
        }
259

260
        continue;
261
      } else if (sv.back() == ']') {
3,965✔
262
        throw std::runtime_error(f + ":" + std::to_string(lineNum) +
×
263
                                 ": expedted [ at the start");
×
264
      }
265

266
      const size_t foundEq = sv.find('=');
3,793✔
267

268
      std::string_view key(es::TrimWhitespace(sv.substr(0, foundEq)));
3,793✔
269
      std::string_view value;
3,793✔
270

271
      if (foundEq != sv.npos) {
3,793✔
272
        value = es::TrimWhitespace(sv.substr(foundEq + 1));
3,208✔
273

274
        if (value.empty()) {
3,208✔
275
          throw std::runtime_error(f + ":" + std::to_string(lineNum) +
×
276
                                   ": expedted value after =");
×
277
        }
278
      }
279

280
      executors[uint32(curFamily)](curPlatform, key, value);
3,793✔
281

282
      if (curFamily == revil__Family::ARC) {
3,793✔
283
        usedArcs[uint32(curPlatform)] = true;
15✔
284
      }
285
    }
286

287
    if (usedArcs[0]) {
29✔
288
      for (uint32 p = 1; p < NUM_PLATFORMS; p++) {
99✔
289
        if (!usedArcs[p] && GLOBAL_DB.back().usedPlatforms[p]) {
90✔
290
          GLOBAL_DB.back().arcSuport[p] = GLOBAL_DB.back().arcSuport[0];
291
        }
292
      }
293
    }
294
  }
29✔
295
}
1✔
296

297
using CompareString = decltype([](auto &i1, auto &i2) {
125,523✔
298
  if (i1.size() == i2.size()) {
125,523✔
299
    return i1 < i2;
96,842✔
300
  }
301

302
  return i1.size() > i2.size();
28,681✔
303
});
304

122,512✔
305
auto DoStrings(BinWritterRef wr) {
122,512✔
306
  std::map<std::string_view, uint32> sliderOffsets;
93,893✔
307
  std::set<std::string_view, CompareString> strings;
308

309
  for (auto &d : GLOBAL_DB) {
28,619✔
310
    for (auto &t : d.titles) {
311
      strings.emplace(t);
3,011✔
312
    }
3,011✔
313

2,949✔
314
    for (auto &c : d.classes) {
315
      for (auto &i : c) {
316
        strings.emplace(i.name);
62✔
317

318
        if (i.extension.size() > 0) {
319
          strings.emplace(i.extension);
1✔
320
        }
321
      }
322
    }
323

30✔
324
    for (auto &c : d.otherClasses) {
96✔
325
      strings.emplace(c);
326
    }
327
  }
328

348✔
329
  StringSlider slider;
3,379✔
330
  const uint32 stringsBegin = wr.Tell();
3,060✔
331

332
  for (auto s : strings) {
3,060✔
333
    sliderOffsets.emplace(s, slider.InsertString(s) + stringsBegin);
3,060✔
334
  }
335

336
  for (auto &d : GLOBAL_DB) {
337
    for (uint32 p = 0; p < NUM_PLATFORMS; p++) {
338
      auto &key = d.arcSuport[p].key;
611✔
339
      if (key.size() > 0) {
340
        sliderOffsets.emplace(key, slider.InsertString(key) + stringsBegin);
341
      }
342
    }
343
  }
344

345
  wr.WriteContainer(slider.buffer);
346
  return sliderOffsets;
3,920✔
347
}
3,919✔
348

349
Header DoClasses(BinWritterRef wr,
350
                 const std::map<std::string_view, uint32> &sliderOffsets) {
30✔
351
  struct ExtInfo {
348✔
352
    std::map<uint32, uint32> platformWeights;
319✔
353
  };
319✔
354

4✔
355
  struct ClassInfo {
356
    std::map<std::string_view, ExtInfo> extensions;
357
    bool version21[2]{};
358
  };
359

360
  struct UClassInfo {
1✔
361
    bool version21[2]{};
362
  };
363

1✔
364
  std::map<std::string_view, ClassInfo> sortedClasses;
365
  std::map<std::string_view, UClassInfo> sortedUClasses;
366
  std::map<std::string_view, std::set<std::string_view>, CompareString>
367
      sortedExtensions;
368

369
  for (auto &d : GLOBAL_DB) {
1,865✔
370
    uint32 curPlatform = 0;
371
    for (auto &c : d.classes) {
372
      for (auto &i : c) {
373
        sortedClasses[i.name].version21[d.version1] = true;
374

462✔
375
        if (i.extension.size() > 0) {
376
          sortedClasses[i.name]
377
              .extensions[i.extension]
378
              .platformWeights[curPlatform]++;
379

380
          sortedExtensions[i.extension].emplace(i.name);
381
        }
382
      }
383

30✔
384
      curPlatform++;
29✔
385
    }
348✔
386

3,379✔
387
    for (auto &c : d.otherClasses) {
3,060✔
388
      sortedUClasses[c].version21[d.version1] = true;
389
    }
3,060✔
390
  }
3,060✔
391

3,060✔
392
  wr.ApplyPadding(4);
3,060✔
393
  std::vector<ResourceClass> rClasses;
394

3,060✔
395
  auto NewResClass = [&](auto &c, bool v1Hash) {
396
    auto &res = rClasses.emplace_back();
397

398
    if (v1Hash) {
319✔
399
      res.hash = MTHashV1(c.first);
400
    } else {
401
      res.hash = MTHashV2(c.first);
611✔
402
    }
582✔
403

404
    res.name.offset = sliderOffsets.at(c.first);
405
    res.name.size = c.first.size();
406

1✔
407
    if (c.second.extensions.size() == 1) {
1✔
408
      res.extension.offset =
409
          sliderOffsets.at(c.second.extensions.begin()->first);
1,910✔
410
      res.extension.size = c.second.extensions.begin()->first.size();
1,910✔
411
    } else {
412
      std::string_view mostAuto;
1,910✔
413
      size_t maxWeightAuto = 0;
89✔
414
      std::string_view mostOther;
415
      size_t maxWeightOther = 0;
1,821✔
416

417
      for (auto &[ex, plt] : c.second.extensions) {
418
        for (auto &[p, w] : plt.platformWeights) {
1,910✔
419
          if (p == 0) {
1,910✔
420
            if (w > maxWeightAuto) {
421
              maxWeightAuto = w;
1,910✔
422
              mostAuto = ex;
1,869✔
423
            }
1,869✔
424
          } else if (w > maxWeightOther) {
1,869✔
425
            maxWeightOther = w;
426
            mostOther = ex;
41✔
427
          }
428
        }
41✔
429
      }
430

431
      if (maxWeightAuto > 0) {
128✔
432
        res.extension.offset = sliderOffsets.at(mostAuto);
268✔
433
        res.extension.size = mostAuto.size();
181✔
434
      } else {
59✔
435
        res.extension.offset = sliderOffsets.at(mostOther);
436
        res.extension.size = mostOther.size();
35✔
437
      }
438
    }
122✔
439
  };
440

45✔
441
  for (auto &c : sortedClasses) {
442
    if (c.second.extensions.size() > 0) {
443

444
      if (c.second.version21[0]) {
445
        NewResClass(c, false);
41✔
446
      }
34✔
447

34✔
448
      if (c.second.version21[1]) {
449
        NewResClass(c, true);
7✔
450
      }
7✔
451
    }
452
  }
453
  auto WriteClasses = [&] {
1✔
454
    std::sort(rClasses.begin(), rClasses.end(),
455
              [](auto &c1, auto &c2) { return c1.hash < c2.hash; });
1,866✔
456

1,865✔
457
    for (auto &c : rClasses) {
458
      const int32 namePtrOffset = offsetof(ResourceClass, name) + wr.Tell();
1,865✔
459
      c.name.offset -= namePtrOffset;
1,821✔
460
      const int32 extPtrOffset = offsetof(ResourceClass, extension) + wr.Tell();
461
      c.extension.offset -= extPtrOffset;
462

1,865✔
463
      wr.Write(c);
89✔
464
    }
465

466
    es::Dispose(rClasses);
467
  };
11✔
468

11✔
469
  Header hdr{};
24,753✔
470
  hdr.resourceClasses[0].data.varPtr = wr.Tell() - 8;
471
  hdr.resourceClasses[0].numItems = rClasses.size();
2,023✔
472
  WriteClasses();
2,012✔
473

2,012✔
474
  auto NewResClassPlt = [&](auto &c, bool v1Hash, uint32 platform) {
2,012✔
475
    std::string_view mostAuto;
2,012✔
476
    size_t maxWeightAuto = 0;
477
    std::string_view mostOther;
478
    size_t maxWeightOther = 0;
479

480
    for (auto &[ex, plt] : c.second.extensions) {
11✔
481
      for (auto &[p, w] : plt.platformWeights) {
12✔
482
        if (p == 0) {
483
          if (w > maxWeightAuto) {
1✔
484
            maxWeightAuto = w;
1✔
485
            mostAuto = ex;
1✔
486
          }
1✔
487
        } else if (w > maxWeightOther) {
488
          maxWeightOther = w;
410✔
489
          mostOther = ex;
490
        }
491
      }
492
    }
493

494
    std::string_view mostExt;
1,280✔
495
    size_t maxWeight = 0;
2,680✔
496

1,810✔
497
    for (auto &[ex, plt] : c.second.extensions) {
590✔
498
      for (auto &[p, w] : plt.platformWeights) {
499
        if (p == platform) {
350✔
500
          if (w > maxWeight) {
501
            maxWeight = w;
1,220✔
502
            mostExt = ex;
503
          }
450✔
504
        }
505
      }
506
    }
507

508
    if (mostExt.empty() || (mostAuto.empty() && mostExt == mostOther)) {
410✔
509
      return;
510
    }
511

1,280✔
512
    auto &res = rClasses.emplace_back();
2,680✔
513

1,810✔
514
    if (v1Hash) {
122✔
515
      res.hash = MTHashV1(c.first);
516
    } else {
115✔
517
      res.hash = MTHashV2(c.first);
518
    }
519

520
    res.name.offset = sliderOffsets.at(c.first);
521
    res.name.size = c.first.size();
522

425✔
523
    res.extension.offset = sliderOffsets.at(mostExt);
308✔
524
    res.extension.size = mostExt.size();
525
  };
526

102✔
527
  for (uint32 p = 1; p < NUM_PLATFORMS; p++) {
528
    for (auto &c : sortedClasses) {
102✔
529
      auto &exts = c.second.extensions;
36✔
530

531
      if (exts.size() > 1) {
66✔
532
        if (c.second.version21[0]) {
533
          NewResClassPlt(c, false, p);
534
        }
102✔
535

102✔
536
        if (c.second.version21[1]) {
537
          NewResClassPlt(c, true, p);
102✔
538
        }
102✔
539
      }
1✔
540
    }
541

11✔
542
    hdr.resourceClasses[p].data.varPtr = wr.Tell() - (8 * p) - 8;
18,660✔
543
    hdr.resourceClasses[p].numItems = rClasses.size();
544
    WriteClasses();
545
  }
18,650✔
546

320✔
547
  es::Dispose(rClasses);
300✔
548

549
  std::vector<Class> uClasses;
550

320✔
551
  for (auto &[name, info] : sortedUClasses) {
110✔
552
    if (info.version21[0]) {
553
      auto &res = uClasses.emplace_back();
554
      res.hash = MTHashV2(name);
555
      res.name = {{
556
          .size = uint32(name.size()),
10✔
557
          .offset = int32(sliderOffsets.at(name)),
10✔
558
      }};
10✔
559
    }
560

561
    if (info.version21[1]) {
1✔
562
      auto &res = uClasses.emplace_back();
563
      res.hash = MTHashV1(name);
1✔
564
      res.name = {{
565
          .size = uint32(name.size()),
463✔
566
          .offset = int32(sliderOffsets.at(name)),
462✔
567
      }};
391✔
568
    }
391✔
569
  }
782✔
570

571
  std::sort(uClasses.begin(), uClasses.end(),
391✔
572
            [](auto &c1, auto &c2) { return c1.hash < c2.hash; });
573

574
  hdr.classes.data.varPtr = wr.Tell() - offsetof(Header, classes.data);
575
  hdr.classes.numItems = uClasses.size();
462✔
576

114✔
577
  for (auto &c : uClasses) {
114✔
578
    const int32 namePtrOffset = offsetof(Class, name) + wr.Tell();
228✔
579
    c.name.offset -= namePtrOffset;
580
    wr.Write(c);
114✔
581
  }
582

583
  es::Dispose(uClasses);
584
  // std::map<std::string_view, uint8, CompareString> extNs;
585
  std::set<std::string_view, CompareString> extNs;
1✔
586

5,514✔
587
  for (uint32 v = 0; v < 2; v++) {
588
    hdr.extensions[v].data.varPtr =
1✔
589
        wr.Tell() -
1✔
590
        (offsetof(Header, extensions[0].data) + sizeof(Array<Extension>) * v);
591

506✔
592
    for (auto &[ext, cls] : sortedExtensions) {
505✔
593
      std::vector<std::string_view> classes;
505✔
594

595
      for (auto &c : cls) {
596
        auto &cInfo = sortedClasses.at(c);
597
        if (cInfo.version21[v]) {
1✔
598
          classes.emplace_back(c);
599
        }
600
      }
601

3✔
602
      if (classes.size() != 1) {
2✔
603
        continue;
2✔
604
      }
605

606
      hdr.extensions[v].numItems++;
3,056✔
607

3,054✔
608
      wr.Write(Extension{
609
          .extension{{
6,858✔
610
              .size = uint32(ext.size()),
3,804✔
611
              .offset = int32(sliderOffsets.at(ext) - wr.Tell()),
3,804✔
612
          }},
1,956✔
613
          .hash = v ? MTHashV1(classes.front()) : MTHashV2(classes.front()),
614
      });
615
    }
616

3,054✔
617
    hdr.extensions4[v].data.varPtr =
618
        wr.Tell() -
619
        (offsetof(Header, extensions4[0].data) + sizeof(Array<Extension>) * v);
620

1,330✔
621
    for (auto &[ext, cls] : sortedExtensions) {
622
      std::vector<std::string_view> classes;
2,660✔
623

624
      for (auto &c : cls) {
625
        auto &cInfo = sortedClasses.at(c);
1,330✔
626
        if (cInfo.version21[v]) {
627
          classes.emplace_back(c);
1,330✔
628
        }
629
      }
630

631
      if (classes.size() < 2) {
2✔
632
        continue;
2✔
633
      }
634

635
      if (classes.size() > 4) {
3,056✔
636
        if (v == 0) {
3,054✔
637
          extNs.emplace(ext);
638
        } else {
6,858✔
639
          throw es::RuntimeError("ExtensionN not implemented for version 1");
3,804✔
640
        }
3,804✔
641

1,956✔
642
        // extNs[ext] |= 1 << v;
643
        continue;
644
      }
645

3,054✔
646
      hdr.extensions4[v].numItems++;
2,789✔
647

648
      Extension4 ext4{
649
          .extension{{
265✔
650
              .size = uint32(ext.size()),
6✔
651
              .offset = int32(sliderOffsets.at(ext) - wr.Tell()),
652
          }},
653
          .hashes{},
×
654
      };
655

656
      for (size_t c = 0; c < classes.size(); c++) {
657
        ext4.hashes[c] = v ? MTHashV1(classes.at(c)) : MTHashV2(classes.at(c));
6✔
658
      }
659

660
      wr.Write(ext4);
259✔
661
    }
662
  }
259✔
663

664
  std::vector<SmallArray<uint32>> hashesN;
665

259✔
666
  /*for (auto &[ext, versions] : extNs) {
667
    SmallArray<uint32> curItem{
668
        .size = 0,
259✔
669
        .offset = int32(wr.Tell()),
670
    };
832✔
671

573✔
672
    auto &cls = sortedExtensions.at(ext);
673

674
    for (auto &c : cls) {
675
      auto &cInfo = sortedClasses.at(c);
676
      for (uint32 v = 0; v < 2; v++) {
677
        if ((versions & (1 << v)) && cInfo.version21[v]) {
678
          wr.Write(v ? MTHashV1(c) : MTHashV2(c));
1✔
679
          curItem.size++;
680
        }
681
      }
682
    }
683

684
    hashesN.emplace_back(curItem);
685
  }*/
686

687
  for (auto ext : extNs) {
688
    SmallArray<uint32> curItem{
689
        .size = 0,
690
        .offset = int32(wr.Tell()),
691
    };
692

693
    auto &cls = sortedExtensions.at(ext);
694

695
    for (auto &c : cls) {
696
      wr.Write(MTHashV2(c));
697
      curItem.size++;
698
    }
699

700
    hashesN.emplace_back(curItem);
701
  }
7✔
702

6✔
703
  hdr.extensionsN.data.varPtr = wr.Tell() - offsetof(Header, extensionsN.data);
704
  hdr.extensionsN.numItems = hashesN.size();
6✔
705

6✔
706
  for (auto keys = extNs.begin(); auto &arr : hashesN) {
707
    ExtensionN ext{
6✔
708
        .extension{{
709
            .size = uint32(keys->size()),
59✔
710
            .offset = int32(sliderOffsets.at(*keys) - wr.Tell()),
53✔
711
        }},
53✔
712
        .hashes = arr,
713
    };
714
    ext.hashes.offset -= wr.Tell() + offsetof(ExtensionN, hashes);
6✔
715
    wr.Write(ext);
716

717
    keys++;
1✔
718
  }
1✔
719

720
  return hdr;
7✔
721
}
722

723
size_t DoSupport(BinWritterRef wr,
724
                 const std::map<std::string_view, uint32> &sliderOffsets) {
6✔
725
  std::map<TitleSupport, uint32> supportPalette;
726

727
  auto DbArc = [&sliderOffsets](auto &t, uint32 p) {
12✔
728
    DbArcSupport arc{
6✔
729
        .version = t.arcSuport[p].version,
730
        .windowSize = t.arcSuport[p].windowSize,
731
        .flags =
732
            uint8(t.arcSuport[p].allowRaw |
733
                  (int(t.arcSuport[p].extendedFilePath) << 1) |
734
                  (int(t.arcSuport[p].xmemOnly) << 2) | (int(t.version1) << 3)),
1✔
735
        .key = {},
736
    };
737

1✔
738
    if (auto &key = t.arcSuport[p].key; key.size() > 0) {
739
      arc.key = {{
740
          .size = uint8(key.size()),
741
          .offset = int32(sliderOffsets.at(key)),
46✔
742
      }};
44✔
743
    }
44✔
744

44✔
745
    return arc;
746
  };
44✔
747

44✔
748
  std::vector<std::array<int32, NUM_PLATFORMS - 1>> indicesPalette;
44✔
749

750
  for (auto &t : GLOBAL_DB) {
751
    std::array<int32, NUM_PLATFORMS - 1> indices;
752

44✔
753
    for (uint32 p = 1; p < NUM_PLATFORMS; p++) {
2✔
754
      if (!t.usedPlatforms[p]) {
755
        indices[p - 1] = -1;
2✔
756
        continue;
757
      }
758

759
      TitleSupport supp{
44✔
760
          .arc = t.arcSuport[p].version ? DbArc(t, p) : DbArc(t, 0),
1✔
761
          .modVersion = t.modVersions[p] ? t.modVersions[p] : t.modVersions[0],
762
          .lmtVersion = t.lmtVersions[p] ? t.lmtVersions[p] : t.lmtVersions[0],
1✔
763
          .texVersion = t.texVersions[p] ? t.texVersions[p] : t.texVersions[0],
764
          .xfsVersion = t.xfsVersions[p] ? t.xfsVersions[p] : t.xfsVersions[0],
30✔
765
          .sdlVersion = t.sdlVersions[p] ? t.sdlVersions[p] : t.sdlVersions[0],
766
      };
767

319✔
768
      if (auto found = supportPalette.find(supp);
290✔
769
          found == supportPalette.end()) {
246✔
770
        indices[p - 1] = supportPalette.size();
246✔
771
        supportPalette.emplace(supp, supportPalette.size());
772
      } else {
773
        indices[p - 1] = found->second;
774
      }
44✔
775
    }
44✔
776

40✔
777
    indicesPalette.emplace_back(indices);
32✔
778
  }
44✔
779

44✔
780
  const size_t supportPaletteBegin = wr.Tell();
204✔
781

782
  {
44✔
783
    std::vector<TitleSupport> sortedSupports;
784
    sortedSupports.resize(supportPalette.size());
33✔
785

33✔
786
    for (auto &[t, i] : supportPalette) {
787
      sortedSupports.at(i) = t;
11✔
788
    }
789

790
    for (auto &s : sortedSupports) {
791
      if (s.arc.key.size > 0) {
29✔
792
        s.arc.key.offset -= wr.Tell() + offsetof(TitleSupport, arc.key);
793
      }
794

795
      wr.Write(s);
796
    }
797
  }
1✔
798

1✔
799
  const size_t indicesBegin = wr.Tell();
800

34✔
801
  for (auto i : indicesPalette) {
33✔
802
    for (auto &t : i) {
803
      if (t < 0) {
804
        t = 0;
34✔
805
      } else {
33✔
806
        t = (supportPaletteBegin + t * sizeof(TitleSupport)) - wr.Tell();
2✔
807
      }
808
      wr.Write(t);
809
    }
810
  }
811

812
  return indicesBegin;
813
}
814

815
std::vector<size_t>
30✔
816
DoTitleFixups(BinWritterRef wr, size_t indicesBegin, const Header *hdr,
319✔
817
              const std::map<std::string_view, uint32> &sliderOffsets) {
290✔
818
  std::vector<size_t> titleOffsets;
246✔
819
  auto &aClasses = hdr->resourceClasses[0];
820
  int32 curTitle = -1;
44✔
821

822
  for (auto &d : GLOBAL_DB) {
823
    curTitle++;
824
    std::map<uint32, std::vector<ResourceClass>> fixups;
825

826
    for (uint32 p = 0; p < NUM_PLATFORMS; p++) {
1✔
827
      if (!d.usedPlatforms[p] && p > 0) {
828
        continue;
829
      }
830

1✔
831
      auto &sClasses = hdr->resourceClasses[p];
832

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

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

29✔
838
        if (found == sClasses.end() || found->hash != hash) {
839
          found = std::lower_bound(aClasses.begin(), aClasses.end(), hash);
840

348✔
841
          if (found == aClasses.end()) {
319✔
842
            throw es::RuntimeError("Cannot find class");
246✔
843
          }
844
        }
845

73✔
846
        if (found->hash != hash) {
847
          throw es::RuntimeError("Cannot find class");
3,133✔
848
        }
3,060✔
849

850
        if (std::string_view(found->extension) != c.extension) {
851
          fixups[p].emplace_back(ResourceClass{
852
              Class{
3,060✔
853
                  .hash = hash,
854
                  .name = {{
855
                      .size = uint32(c.name.size()),
1,723✔
856
                      .offset = int32(sliderOffsets.at(c.name)),
×
857
                  }},
858
              },
859
              {{
860
                  .size = uint32(c.extension.size()),
3,060✔
861
                  .offset = int32(sliderOffsets.at(c.extension)),
×
862
              }},
863
          });
864
        }
3,014✔
865

46✔
866
        if (auto classExtensions =
867
                ClassesFromExtension(*hdr, c.extension, d.version1);
868
            classExtensions.size() > 1) {
869
          fixups[p].emplace_back(ResourceClass{
870
              Class{
46✔
871
                  .hash = hash,
872
                  .name = {{
873
                      .size = uint32(c.name.size()),
874
                      .offset = int32(sliderOffsets.at(c.name)),
875
                  }},
46✔
876
              },
877
              {{
878
                  .size = uint32(c.extension.size()),
879
                  .offset = int32(sliderOffsets.at(c.extension)),
880
              }},
3,060✔
881
          });
3,060✔
882
        }
883
      }
906✔
884
    }
885

886
    std::vector<Fixup> platformFixups;
887

888
    for (auto &[plt, fix] : fixups) {
906✔
889
      std::sort(fix.begin(), fix.end(),
890
                [](auto &c1, auto &c2) { return c1.hash < c2.hash; });
891
      platformFixups.emplace_back(Fixup{
892
          .classes{.varPtr = int32(wr.Tell())},
893
          .platform = uint8(plt),
906✔
894
          .numItems = uint16(fix.size()),
895
      });
896

897
      for (auto &c : fix) {
898
        const int32 namePtrOffset = offsetof(ResourceClass, name) + wr.Tell();
899
        c.name.offset -= namePtrOffset;
900
        const int32 extPtrOffset =
29✔
901
            offsetof(ResourceClass, extension) + wr.Tell();
902
        c.extension.offset -= extPtrOffset;
75✔
903

46✔
904
        wr.Write(c);
6,011✔
905
      }
46✔
906
    }
907

46✔
908
    const int32 fixupsBegin = wr.Tell();
909

910
    for (auto &f : platformFixups) {
911
      f.classes.varPtr -= wr.Tell();
998✔
912
      wr.Write(f);
952✔
913
    }
952✔
914

915
    Title title{
952✔
916
        .support{.varPtr = int32(indicesBegin +
952✔
917
                                 curTitle * sizeof(int32[NUM_PLATFORMS - 1])) -
918
                           int32(wr.Tell() - offsetof(Title, support))},
919
        .fixups{.size = uint32(platformFixups.size()),
920
                .offset =
921
                    (platformFixups.size() > 0) *
922
                    int32(fixupsBegin - wr.Tell() - offsetof(Title, fixups))},
923
    };
924

75✔
925
    titleOffsets.emplace_back(wr.Tell());
46✔
926
    wr.Write(title);
927
  }
928

929
  return titleOffsets;
930
}
29✔
931

29✔
932
void ValidateDb(const Header *shdr) {
933
  for (auto &d : GLOBAL_DB) {
934
    for (auto &t : d.titles) {
935
      auto oKey = LowerBound(t, shdr->titles);
58✔
936

29✔
937
      if (oKey == shdr->titles.end() || std::string_view(oKey->name) != t) {
29✔
938
        throw es::RuntimeError("Cannot find title");
939
      }
29✔
940

941
      const Title &title = oKey->data;
942

943
      auto supports = title.support.operator->();
1✔
944

945
      for (size_t p = 1; p < NUM_PLATFORMS; p++) {
946
        if (!d.usedPlatforms[p]) {
1✔
947
          if (supports[p - 1].operator->()) {
30✔
948
            throw es::RuntimeError("Validation failed");
96✔
949
          }
67✔
950

951
          continue;
134✔
952
        }
×
953

954
        const TitleSupport &support = supports[p - 1];
955

956
        if (support.arc.version != d.arcSuport[p].version) {
957
          throw es::RuntimeError("Validation failed");
958
        }
959

737✔
960
        if (support.arc.windowSize != d.arcSuport[p].windowSize) {
670✔
961
          throw es::RuntimeError("Validation failed");
565✔
962
        }
×
963

964
        if (std::string_view(support.arc.key) != d.arcSuport[p].key) {
965
          throw es::RuntimeError("Validation failed");
565✔
966
        }
967

968
        if (bool(support.arc.flags & DbArc_AllowRaw) !=
105✔
969
            d.arcSuport[p].allowRaw) {
970
          throw es::RuntimeError("Validation failed");
105✔
971
        }
×
972

973
        if (bool(support.arc.flags & DbArc_ExtendedPath) !=
974
            d.arcSuport[p].extendedFilePath) {
105✔
975
          throw es::RuntimeError("Validation failed");
×
976
        }
977

978
        if (bool(support.arc.flags & DbArc_XMemCompress) !=
105✔
979
            d.arcSuport[p].xmemOnly) {
×
980
          throw es::RuntimeError("Validation failed");
981
        }
982

105✔
983
        if (bool(support.arc.flags & DbArc_Version1) != d.version1) {
105✔
984
          throw es::RuntimeError("Validation failed");
×
985
        }
986

987
        if (support.lmtVersion !=
105✔
988
            (d.lmtVersions[p] ? d.lmtVersions[p] : d.lmtVersions[0])) {
105✔
989
          throw es::RuntimeError("Validation failed");
×
990
        }
991

992
        if (support.modVersion !=
105✔
993
            (d.modVersions[p] ? d.modVersions[p] : d.modVersions[0])) {
105✔
994
          throw es::RuntimeError("Validation failed");
×
995
        }
996

997
        if (support.texVersion !=
105✔
998
            (d.texVersions[p] ? d.texVersions[p] : d.texVersions[0])) {
×
999
          throw es::RuntimeError("Validation failed");
1000
        }
1001

105✔
1002
        if (support.xfsVersion !=
105✔
1003
            (d.xfsVersions[p] ? d.xfsVersions[p] : d.xfsVersions[0])) {
×
1004
          throw es::RuntimeError("Validation failed");
1005
        }
1006

105✔
1007
        if (support.sdlVersion !=
105✔
1008
            (d.sdlVersions[p] ? d.sdlVersions[p] : d.sdlVersions[0])) {
×
1009
          throw es::RuntimeError("Validation failed");
1010
        }
1011
      }
105✔
1012

105✔
1013
      for (size_t p = 0; p < NUM_PLATFORMS; p++) {
×
1014
        auto &rClasses = d.classes[p];
1015

1016
        for (auto &c : rClasses) {
105✔
1017
          const uint32 hash = d.version1 ? MTHashV1(c.name) : MTHashV2(c.name);
105✔
1018
          bool found = false;
×
1019

1020
          for (auto &f : title.fixups) {
1021
            if (f.platform == p) {
105✔
1022
              [&] {
105✔
1023
                const ResourceClass *fClasses = f.classes.operator->();
×
1024

1025
                for (size_t i = 0; i < f.numItems; i++) {
1026
                  if (fClasses[i].hash == hash) {
1027
                    if (std::string_view(fClasses[i].extension) !=
804✔
1028
                        c.extension) {
737✔
1029
                      throw es::RuntimeError("Validation error");
1030
                    }
8,743✔
1031
                    found = true;
8,006✔
1032
                    return;
8,006✔
1033
                  }
1034
                }
23,212✔
1035
                return;
15,206✔
1036
              }();
7,826✔
1037
            }
7,826✔
1038
          }
1039

378,055✔
1040
          if (found) {
372,629✔
1041
            continue;
1042
          }
2,400✔
1043

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

2,400✔
1046
          if (foundClass == shdr->resourceClasses[p].end() ||
2,400✔
1047
              foundClass->hash != hash) {
1048
            foundClass = LowerBound(hash, shdr->resourceClasses[0]);
1049

1050
            if (foundClass == shdr->resourceClasses[0].end()) {
7,826✔
1051
              throw es::RuntimeError("Validation error");
1052
            }
1053
          }
1054

8,006✔
1055
          if (std::string_view(foundClass->extension) != c.extension) {
2,400✔
1056
            throw es::RuntimeError("Validation error");
1057
          }
1058

5,606✔
1059
          [&] {
1060
            for (auto cf :
5,606✔
1061
                 ClassesFromExtension(*shdr, c.extension, d.version1)) {
5,602✔
1062
              if (cf == hash) {
1063
                return;
1064
              }
2,503✔
1065
            }
×
1066

1067
            throw es::RuntimeError("Validation error");
1068
          }();
1069
        }
5,606✔
1070
      }
×
1071
    }
1072

1073
    for (auto &c : d.otherClasses) {
5,606✔
1074
      const uint32 hash = d.version1 ? MTHashV1(c) : MTHashV2(c);
5,606✔
1075
      auto found = LowerBound(hash, shdr->classes);
5,606✔
1076

5,606✔
1077
      if (found == shdr->classes.end() || found->hash != hash ||
5,606✔
1078
          std::string_view(found->name) != c) {
1079
        throw es::RuntimeError("Validation error");
1080
      }
1081
    }
×
1082
  }
5,606✔
1083
}
1084

1085
int main() {
1086
  LoadDb();
1087

611✔
1088
  std::stringstream str;
582✔
1089
  BinWritterRef wr(str);
582✔
1090
  wr.Write(Header{});
1091

582✔
1092
  std::map<std::string_view, uint32> sliderOffsets = DoStrings(wr);
582✔
1093
  Header hdr = DoClasses(wr, sliderOffsets);
×
1094
  const size_t indicesBegin = DoSupport(wr, sliderOffsets);
1095

1096
  wr.Push();
1097
  wr.Seek(0);
1✔
1098
  wr.Write(hdr);
1099
  wr.Pop();
1✔
1100

1✔
1101
  auto titleOffsets = DoTitleFixups(
1102
      wr, indicesBegin,
1✔
1103
      reinterpret_cast<const Header *>(str.rdbuf()->view().data()),
1104
      sliderOffsets);
1✔
1105

1106
  std::map<std::string_view, size_t, CompareString> titles;
1✔
1107

1✔
1108
  size_t curTitle = 0;
1✔
1109
  for (auto &d : GLOBAL_DB) {
1110
    for (auto &t : d.titles) {
1111
      titles.emplace(t, titleOffsets.at(curTitle));
1112
    }
1113
    curTitle++;
1114
  }
1115

1116
  hdr.titles.numItems = titles.size();
1117
  hdr.titles.data.varPtr = wr.Tell() - offsetof(Header, titles);
1118

1✔
1119
  for (auto &[tit, off] : titles) {
1120
    TitleName res{
1121
        .name{{
1122
            .size = uint32(tit.size()),
1123
            .offset = int32(sliderOffsets.at(tit) - wr.Tell() -
30✔
1124
                            offsetof(TitleName, name)),
96✔
1125
        }},
1126
        .data{.varPtr = int32(off - wr.Tell() - offsetof(TitleName, data))},
1127
    };
29✔
1128

1129
    wr.Write(res);
1130
  }
1✔
1131

1✔
1132
  wr.Seek(0);
1133
  wr.Write(hdr);
68✔
1134

1135
  // BinWritter wrn("database/redb");
1136
  // wrn.WriteContainer(str.rdbuf()->view());
1137

67✔
1138
  const uint64 *oData =
1139
      reinterpret_cast<const uint64 *>(str.rdbuf()->view().data());
1140
  const size_t numItems = (str.rdbuf()->view().size() + 7) / 8;
134✔
1141
  std::ofstream ostr("database/redb.c");
134✔
1142

1143
  try {
1144
    ValidateDb(reinterpret_cast<const Header *>(str.rdbuf()->view().data()));
1145
  } catch (const std::exception &e) {
1146
    ostr << "#pragma error \"" << e.what() << "\"" << std::endl;
1147
    throw;
1148
  }
1149

1150
  // C23 uses embed macro, wait for compiler support
1151
  ostr << "#include <stdint.h>\nconst uint64_t REDB[] "
1152
          "__attribute__((section(\".redb\"))) = {"
1153
       << std::hex;
1154

1✔
1155
  for (size_t i = 0; i < numItems; i++) {
1✔
1156
    ostr << "0x" << oData[i] << ',';
1157
  }
1158

1✔
1159
  ostr << "};\n";
×
1160

×
1161
  return 0;
×
1162
}
×
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