mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-11 01:41:02 +00:00
Load 1.4.3 modules: DT_RELR, symbol count from DT_GNU_HASH, stg as a no-op, version-aware boot anchors and loader probe values
The jump-table check now explains oracle targets for case indices peeled off by cmp #k; b.ne before the table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ba2b3f439f
commit
776d547169
8 files changed
+141
-29
No files matched your search
@@ -383,8 +383,20 @@ class Lifter:
|
||||
return self.ldst_pair(w)
|
||||
if bits(w, 27, 3) == 0b111:
|
||||
return self.ldst_reg(w)
|
||||
if (w & 0xFFE00000) == 0xD9200000 and bits(w, 10, 2):
|
||||
return self.stg(w)
|
||||
raise Unsupported(f'ldst {w:08x}')
|
||||
|
||||
def stg(self, w):
|
||||
"""STG (post-index, signed offset, pre-index). Horizon runs without MTE, so storing an allocation tag
|
||||
changes no memory; only the base writeback remains (1.4.3 sdk+0x640050)."""
|
||||
mode, rn = bits(w, 10, 2), bits(w, 5, 5)
|
||||
if mode == 2:
|
||||
return Insn('op', '/* stg */')
|
||||
off = sext(bits(w, 12, 9), 9) << 4
|
||||
dst = 'c->sp' if rn == 31 else f'c->x[{rn}]'
|
||||
return Insn('op', f'{dst} = {XS(rn)} + {hx(off & 0xFFFFFFFFFFFFFFFF)}; /* stg */')
|
||||
|
||||
def ldst_excl(self, w, pc):
|
||||
size, o2, L, o1, rs, o0, rn, rt = (bits(w, 30, 2), bits(w, 23, 1), bits(w, 22, 1), bits(w, 21, 1),
|
||||
bits(w, 16, 5), bits(w, 15, 1), bits(w, 5, 5), bits(w, 0, 5))
|
||||
|
||||
+38
-4
@@ -8,6 +8,7 @@ import lz4.block
|
||||
|
||||
DT_NULL, DT_NEEDED, DT_PLTRELSZ, DT_HASH, DT_STRTAB, DT_SYMTAB, DT_RELA, DT_RELASZ = 0, 1, 2, 4, 5, 6, 7, 8
|
||||
DT_STRSZ, DT_JMPREL, DT_RELACOUNT = 10, 23, 0x6FFFFFF9
|
||||
DT_RELRSZ, DT_RELR, DT_GNU_HASH = 0x23, 0x24, 0x6FFFFEF5
|
||||
R_AARCH64_ABS64, R_AARCH64_GLOB_DAT, R_AARCH64_JUMP_SLOT, R_AARCH64_RELATIVE = 257, 1025, 1026, 1027
|
||||
|
||||
|
||||
@@ -61,12 +62,12 @@ class Nso:
|
||||
strtab = dyn.get(DT_STRTAB, 0)
|
||||
self.strtab = strtab
|
||||
self.needed = [self.cstr(strtab + n) for n in needed]
|
||||
# dynsym count from DT_HASH nchain
|
||||
# dynsym count from DT_HASH nchain, or from DT_GNU_HASH (1.4.3 sdk and subsdk0 have no DT_HASH)
|
||||
self.syms = []
|
||||
if DT_HASH in dyn and DT_SYMTAB in dyn:
|
||||
nchain = self.u32(dyn[DT_HASH] + 4)
|
||||
nsyms = self.symbol_count()
|
||||
if nsyms and DT_SYMTAB in dyn:
|
||||
st = dyn[DT_SYMTAB]
|
||||
for i in range(nchain):
|
||||
for i in range(nsyms):
|
||||
o = st + i * 24
|
||||
name, info, other, shndx = struct.unpack_from('<IBBH', self.image, o)
|
||||
value, size = struct.unpack_from('<QQ', self.image, o + 8)
|
||||
@@ -78,6 +79,39 @@ class Nso:
|
||||
for i in range(n):
|
||||
r_off, r_info, r_add = struct.unpack_from('<QQq', self.image, b + i * 24)
|
||||
self.relocs.append((r_off, r_info & 0xFFFFFFFF, r_info >> 32, r_add))
|
||||
# DT_RELR (1.4.3): packed RELATIVE relocations whose addend is the word in place. An even entry is an address
|
||||
# (relocate it, continue after it); an odd entry is a bitmap of the next 63 words.
|
||||
if DT_RELR in dyn:
|
||||
where = 0
|
||||
for i in range(dyn[DT_RELRSZ] // 8):
|
||||
e = self.u64(dyn[DT_RELR] + i * 8)
|
||||
if e & 1 == 0:
|
||||
self.relocs.append((e, R_AARCH64_RELATIVE, 0, self.u64(e)))
|
||||
where = e + 8
|
||||
continue
|
||||
for k in range(63):
|
||||
if e >> (k + 1) & 1:
|
||||
a = where + k * 8
|
||||
self.relocs.append((a, R_AARCH64_RELATIVE, 0, self.u64(a)))
|
||||
where += 63 * 8
|
||||
|
||||
def symbol_count(self):
|
||||
"""Number of .dynsym entries: DT_HASH nchain, else one past the last symbol a DT_GNU_HASH chain reaches."""
|
||||
dyn = self.dyn
|
||||
if DT_HASH in dyn:
|
||||
return self.u32(dyn[DT_HASH] + 4)
|
||||
if DT_GNU_HASH not in dyn:
|
||||
return 0
|
||||
h = dyn[DT_GNU_HASH]
|
||||
nbuckets, symoffset, bloom_size = self.u32(h), self.u32(h + 4), self.u32(h + 8)
|
||||
buckets = h + 16 + bloom_size * 8
|
||||
last = max([self.u32(buckets + 4 * i) for i in range(nbuckets)] + [0])
|
||||
if last < symoffset:
|
||||
return symoffset
|
||||
chains = buckets + 4 * nbuckets
|
||||
while not self.u32(chains + 4 * (last - symoffset)) & 1:
|
||||
last += 1
|
||||
return last + 1
|
||||
|
||||
def cstr(self, off):
|
||||
end = self.image.index(b'\0', off)
|
||||
|
||||
+15
-9
@@ -4,8 +4,10 @@
|
||||
// The boot driver: the guest main thread runs the game's own nn::init::Start
|
||||
// with three host callbacks, the second of which runs DT_INIT of main, subsdk0 and sdk; Start then calls nnMain.
|
||||
// --boot checks two checkpoints on the way and ends at the first nvnBootstrapLoader call:
|
||||
// SetFocusHandlingMode called from main+0xe7a954 in nnMain, every module's constructor flag reads 2
|
||||
// nvnBootstrapLoader called from main+0xe7eb2c in the graphics bring-up; logs the run statistics, exits 0
|
||||
// SetFocusHandlingMode called from main+0xe7a954 (1.4.3: +0xbdb48c) in nnMain, every module's constructor flag
|
||||
// reads 2
|
||||
// nvnBootstrapLoader called from main+0xe7eb2c (1.4.3: +0xbe0360) in the graphics bring-up; logs the run
|
||||
// statistics, exits 0
|
||||
// With --frames N the run continues past that call; rt_frame_presented ends it at the Nth present.
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
@@ -18,13 +20,17 @@
|
||||
#include "vk.h"
|
||||
|
||||
#define SYM_INIT_START "_ZN2nn4init5StartEmmPFvvES2_S2_"
|
||||
#define CHECKPOINT_RET 0xe7a954ull // main: return address of nnMain's SetFocusHandlingMode call (section 0.4)
|
||||
#define NVN_RET 0xe7eb2cull // main: return address of the first nvnBootstrapLoader call
|
||||
// main: return addresses of nnMain's SetFocusHandlingMode call (section 0.4; 1.4.3: MNG scripts/verify_boot.py:9) and
|
||||
// of the first nvnBootstrapLoader call, per game version.
|
||||
#define CHECKPOINT_RET (RT_V143 ? 0xbdb48cull : 0xe7a954ull)
|
||||
#define NVN_RET (RT_V143 ? 0xbe0360ull : 0xe7eb2cull)
|
||||
#define CTOR_DONE 2 // a module's constructor flag after its DT_INIT ran the init_array
|
||||
|
||||
// The init_array sizes of 1.2.1 (section 0.1); MacNativeGames' boot check expects the same.
|
||||
static const struct { const char *name; uint64_t entries; } expected_inits[] = {
|
||||
{"main", 10673}, {"subsdk0", 45}, {"sdk", 116}};
|
||||
// The init_array sizes (1.2.1: section 0.1; 1.4.3: DT_INIT_ARRAYSZ); MacNativeGames' boot check expects the same.
|
||||
typedef struct { const char *name; uint64_t entries; } InitCount;
|
||||
static const InitCount inits_121[] = {{"main", 10673}, {"subsdk0", 45}, {"sdk", 116}};
|
||||
static const InitCount inits_143[] = {{"main", 10717}, {"subsdk0", 45}, {"sdk", 83}};
|
||||
#define expected_inits (RT_V143 ? inits_143 : inits_121)
|
||||
|
||||
static volatile LONG inits_done, checkpoint_seen;
|
||||
|
||||
@@ -63,7 +69,7 @@ static void cb_loader_ready(Cpu *c) {
|
||||
// afterwards (MacNativeGames loader.c:458-462).
|
||||
static void cb_module_inits(Cpu *c) {
|
||||
if (InterlockedCompareExchange(&inits_done, 0, 0)) rt_fatal("boot: module constructors requested twice");
|
||||
for (unsigned k = 0; k < sizeof expected_inits / sizeof *expected_inits; k++) {
|
||||
for (unsigned k = 0; k < sizeof inits_121 / sizeof *inits_121; k++) {
|
||||
const RtLoaded *m = module_named(expected_inits[k].name);
|
||||
uint64_t entries = m->init_array_size / 8;
|
||||
if (!m->init) rt_fatal("boot: %s has no DT_INIT", m->name);
|
||||
@@ -73,7 +79,7 @@ static void cb_module_inits(Cpu *c) {
|
||||
printf("boot: DT_INIT %s state=%u (%llu)\n", m->name, state, (unsigned long long)entries);
|
||||
if (state != CTOR_DONE) rt_fatal("boot: %s DT_INIT left its constructor flag at %u, not 2", m->name, state);
|
||||
if (entries != expected_inits[k].entries)
|
||||
rt_fatal("boot: %s has %llu init_array entries, 1.2.1 has %llu", m->name, (unsigned long long)entries,
|
||||
rt_fatal("boot: %s has %llu init_array entries, this game version has %llu", m->name, (unsigned long long)entries,
|
||||
(unsigned long long)expected_inits[k].entries);
|
||||
}
|
||||
InterlockedExchange(&inits_done, 1);
|
||||
|
||||
@@ -106,10 +106,10 @@ static uint64_t read_ptr(uint64_t a) {
|
||||
uint64_t p = 0;
|
||||
return read_guest(a, &p, 8) ? p : 0;
|
||||
}
|
||||
// 1.2.1: [main+0x4728538] +0x1e8 scene, its component 13 lists the resident actors; the one named "Player" has its
|
||||
// position at +0x2b4 (MacNativeGames world_survey.c, game_version.h TOTK_GAME_V121).
|
||||
// 1.2.1: [main+0x4728538] (1.4.3: [main+0x3add238]) +0x1e8 scene, its component 13 lists the resident actors; the one
|
||||
// named "Player" has its position at +0x2b4 (MacNativeGames world_survey.c, game_version.h TOTK_GAME_V121/V143).
|
||||
static int player_pos(float xyz[3]) {
|
||||
uint64_t module = read_ptr(MOD_MAIN_BASE + 0x4728538), scene = module ? read_ptr(module + 0x1e8) : 0;
|
||||
uint64_t module = read_ptr(MOD_MAIN_BASE + (RT_V143 ? 0x3add238 : 0x4728538)), scene = module ? read_ptr(module + 0x1e8) : 0;
|
||||
int32_t count = 0;
|
||||
if (!scene || !read_guest(scene + 0x50, &count, 4) || count < 14 || count > 256) return 0;
|
||||
uint64_t comps = read_ptr(scene + 0x58), resident = comps ? read_ptr(comps + 13 * 8) : 0;
|
||||
|
||||
@@ -163,6 +163,8 @@ typedef struct { const char *file, *sha256; } RtFileHash;
|
||||
extern const RtFileHash rt_exefs_sha256[]; // tools/data/expected-<version>.json "exefs_sha256"
|
||||
extern const int rt_exefs_sha256_count;
|
||||
extern const char rt_default_exefs[], rt_default_romfs[], rt_default_profile[];
|
||||
extern const int rt_game_version; // the game build: 121 (1.2.1) or 143 (1.4.3)
|
||||
#define RT_V143 (rt_game_version >= 143)
|
||||
|
||||
// ---- kernel (kernel.c, T2)
|
||||
typedef struct RtThread RtThread;
|
||||
|
||||
+67
-9
@@ -19,7 +19,7 @@ uint64_t hostfn_register_observer(const char *name, GuestFn fn, uint64_t origina
|
||||
|
||||
enum { DT_NULL = 0, DT_PLTRELSZ = 2, DT_HASH = 4, DT_STRTAB = 5, DT_SYMTAB = 6, DT_RELA = 7, DT_RELASZ = 8,
|
||||
DT_INIT = 12, DT_FINI = 13, DT_JMPREL = 23, DT_INIT_ARRAY = 25, DT_FINI_ARRAY = 26, DT_INIT_ARRAYSZ = 27,
|
||||
DT_FINI_ARRAYSZ = 28 };
|
||||
DT_FINI_ARRAYSZ = 28, DT_RELRSZ = 0x23, DT_RELR = 0x24, DT_GNU_HASH = 0x6ffffef5 };
|
||||
enum { R_ABS64 = 257, R_GLOB_DAT = 1025, R_JUMP_SLOT = 1026, R_RELATIVE = 1027 };
|
||||
enum { K_RELATIVE, K_ABS64, K_GLOB_DAT, K_JUMP_SLOT, K_COUNT };
|
||||
enum { STB_WEAK = 2, STT_FUNC = 2 };
|
||||
@@ -39,6 +39,8 @@ typedef struct {
|
||||
uint32_t nsyms;
|
||||
const Elf64Rela *rela, *jmprel;
|
||||
uint64_t nrela, njmprel;
|
||||
const uint64_t *relr; // DT_RELR (1.4.3): packed RELATIVE relocations
|
||||
uint64_t nrelr, relr_count;
|
||||
uint32_t *hash, hash_mask; // defined symbols by name
|
||||
Res *res_plain, *res_jump; // per symbol; JUMP_SLOT may bind an observer
|
||||
uint32_t *funcs, nfuncs; // defined functions sorted by value (ldr_describe)
|
||||
@@ -219,12 +221,15 @@ static void parse_dynamic(Mod *m) {
|
||||
uint64_t mod0 = base + rd32(base + 4);
|
||||
if (memcmp((const void *)(uintptr_t)mod0, "MOD0", 4)) rt_fatal("loader: %s has no MOD0 header", m->pub.name);
|
||||
uint64_t dyn = mod0 + (int64_t)(int32_t)rd32(mod0 + 4);
|
||||
uint64_t rela = 0, relasz = 0, jmprel = 0, pltsz = 0, hash = 0;
|
||||
uint64_t rela = 0, relasz = 0, jmprel = 0, pltsz = 0, hash = 0, gnu_hash = 0, relr = 0, relrsz = 0;
|
||||
for (const uint64_t *d = (const uint64_t *)(uintptr_t)dyn; d[0] != DT_NULL; d += 2) {
|
||||
switch (d[0]) {
|
||||
case DT_SYMTAB: m->syms = (const Elf64Sym *)(uintptr_t)(base + d[1]); break;
|
||||
case DT_STRTAB: m->strtab = (const char *)(uintptr_t)(base + d[1]); break;
|
||||
case DT_HASH: hash = base + d[1]; break;
|
||||
case DT_GNU_HASH: gnu_hash = base + d[1]; break;
|
||||
case DT_RELR: relr = base + d[1]; break;
|
||||
case DT_RELRSZ: relrsz = d[1]; break;
|
||||
case DT_RELA: rela = base + d[1]; break;
|
||||
case DT_RELASZ: relasz = d[1]; break;
|
||||
case DT_JMPREL: jmprel = base + d[1]; break;
|
||||
@@ -234,8 +239,26 @@ static void parse_dynamic(Mod *m) {
|
||||
case DT_INIT_ARRAYSZ: m->pub.init_array_size = d[1]; break;
|
||||
}
|
||||
}
|
||||
if (!m->syms || !m->strtab || !hash) rt_fatal("loader: %s lacks DT_SYMTAB, DT_STRTAB or DT_HASH", m->pub.name);
|
||||
m->nsyms = rd32(hash + 4); // nchain
|
||||
if (!m->syms || !m->strtab || (!hash && !gnu_hash))
|
||||
rt_fatal("loader: %s lacks DT_SYMTAB, DT_STRTAB or a symbol hash", m->pub.name);
|
||||
if (hash) {
|
||||
m->nsyms = rd32(hash + 4); // nchain
|
||||
} else {
|
||||
// DT_GNU_HASH only (1.4.3 sdk, subsdk0): one past the last symbol the highest bucket's chain reaches.
|
||||
uint32_t nbuckets = rd32(gnu_hash), symoffset = rd32(gnu_hash + 4), bloom = rd32(gnu_hash + 8);
|
||||
uint64_t buckets = gnu_hash + 16 + 8ull * bloom, chains = buckets + 4ull * nbuckets;
|
||||
uint32_t last = 0;
|
||||
for (uint32_t i = 0; i < nbuckets; i++)
|
||||
if (rd32(buckets + 4ull * i) > last) last = rd32(buckets + 4ull * i);
|
||||
if (last < symoffset) {
|
||||
m->nsyms = symoffset;
|
||||
} else {
|
||||
while (!(rd32(chains + 4ull * (last - symoffset)) & 1)) last++;
|
||||
m->nsyms = last + 1;
|
||||
}
|
||||
}
|
||||
m->relr = (const uint64_t *)(uintptr_t)relr;
|
||||
m->nrelr = relrsz / 8;
|
||||
m->rela = (const Elf64Rela *)(uintptr_t)rela;
|
||||
m->nrela = relasz / sizeof(Elf64Rela);
|
||||
m->jmprel = (const Elf64Rela *)(uintptr_t)jmprel;
|
||||
@@ -363,6 +386,33 @@ static void relocate_table(Mod *m, const Elf64Rela *r, uint64_t n) {
|
||||
}
|
||||
}
|
||||
|
||||
// DT_RELR: an even entry is an offset to relocate (the next bitmap starts after it); an odd entry is a bitmap of the
|
||||
// 63 words that follow. Each relocated word holds its addend, as R_AARCH64_RELATIVE with an implicit addend.
|
||||
static void relocate_relr(Mod *m) {
|
||||
uint64_t base = m->pub.base, where = 0;
|
||||
for (uint64_t i = 0; i < m->nrelr; i++) {
|
||||
uint64_t e = m->relr[i];
|
||||
for (int k = -1; k < 63; k++) {
|
||||
uint64_t off;
|
||||
if (k < 0) {
|
||||
if (e & 1) continue;
|
||||
off = e;
|
||||
where = e + 8;
|
||||
} else {
|
||||
if (!(e & 1) || !((e >> (k + 1)) & 1)) continue;
|
||||
off = where + 8ull * k;
|
||||
}
|
||||
if (off < m->pub.data_off || off > m->pub.size - 8)
|
||||
rt_fatal("loader: %s RELR entry %llu writes +%#llx, outside the data segment", m->pub.name,
|
||||
(unsigned long long)i, (unsigned long long)off);
|
||||
ST64(base + off, base + LD64(base + off));
|
||||
m->kinds[K_RELATIVE]++;
|
||||
m->relr_count++;
|
||||
}
|
||||
if (e & 1) where += 63 * 8;
|
||||
}
|
||||
}
|
||||
|
||||
// Import table statistics: every undefined named symbol, bound by guest definition (overrides aside).
|
||||
static void count_imports(Mod *m) {
|
||||
for (uint32_t i = 1; i < m->nsyms; i++) {
|
||||
@@ -416,6 +466,7 @@ void ldr_load(const char *exefs_dir) {
|
||||
for (int i = 0; i < nmods; i++) {
|
||||
relocate_table(&mods[i], mods[i].rela, mods[i].nrela);
|
||||
relocate_table(&mods[i], mods[i].jmprel, mods[i].njmprel);
|
||||
relocate_relr(&mods[i]);
|
||||
count_imports(&mods[i]);
|
||||
mods[i].pub.ctor_flag = decode_ctor_flag(&mods[i]);
|
||||
}
|
||||
@@ -425,7 +476,7 @@ void ldr_load(const char *exefs_dir) {
|
||||
const Mod *m = &mods[i];
|
||||
rt_log("loaded %-8s at %#llx size %#llx: %llu relocations, %u imports", m->pub.name,
|
||||
(unsigned long long)m->pub.base, (unsigned long long)m->pub.size,
|
||||
(unsigned long long)(m->nrela + m->njmprel), m->imports);
|
||||
(unsigned long long)(m->nrela + m->njmprel + m->relr_count), m->imports);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,16 +566,23 @@ uint64_t ldr_plt_stub(const char *module, const char *name) {
|
||||
}
|
||||
|
||||
// ---- --probe-loader
|
||||
// Expected values, measured on the 1.2.1 modules.
|
||||
static const struct {
|
||||
// Expected values, measured on the 1.2.1 and 1.4.3 modules (1.4.3 relocation totals match MNG v1.4.3-port.md:20).
|
||||
typedef struct {
|
||||
const char *name;
|
||||
uint64_t kinds[K_COUNT], total, sym_import, sym_own;
|
||||
uint32_t imports, to[MOD_COUNT], weak, init_off, init_array;
|
||||
} expect[] = {
|
||||
} Expect;
|
||||
static const Expect expect_121[] = {
|
||||
{"main", {508816, 1415, 32, 641}, 510904, 2088, 0, 675, {0, 16, 647}, 12, 0x30, 10673},
|
||||
{"subsdk0", {1623, 12820, 1030, 3924}, 19397, 6576, 11198, 667, {9, 0, 654}, 4, 0x30, 45},
|
||||
{"sdk", {10917, 13976, 1207, 8932}, 35032, 16, 24099, 13, {8, 0, 0}, 5, 0x30, 116},
|
||||
};
|
||||
static const Expect expect_143[] = {
|
||||
{"main", {499078, 1393, 28, 650}, 501149, 0, 0, 0, {0, 0, 0}, 0, 0x10b4f10, 10717},
|
||||
{"subsdk0", {3424, 10927, 905, 3817}, 19073, 0, 0, 0, {0, 0, 0}, 0, 0x20, 45},
|
||||
{"sdk", {15013, 10135, 1079, 7832}, 34059, 0, 0, 0, {0, 0, 0}, 0, 0x400, 83},
|
||||
};
|
||||
#define expect (RT_V143 ? expect_143 : expect_121)
|
||||
|
||||
static int failures;
|
||||
#define CHECK(cond, ...) do { if (!(cond)) { failures++; printf(" FAIL: " __VA_ARGS__); putchar('\n'); } } while (0)
|
||||
@@ -614,7 +672,7 @@ int probe_loader(void) {
|
||||
const RtConfig *cfg = rt_config();
|
||||
hostfn_add_override(PROBE_SYMBOL, probe_override);
|
||||
ldr_load(cfg->exefs);
|
||||
if (nmods != (int)(sizeof expect / sizeof *expect)) {
|
||||
if (nmods != (int)(sizeof expect_121 / sizeof *expect_121)) {
|
||||
printf("probe-loader: FAILED (%d modules, expected 3)\n", nmods);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Binary file not shown.
+4
-4
@@ -187,9 +187,9 @@ def runtime_object(out, src):
|
||||
return out / 'obj' / 'runtime' / rel.with_suffix('.o')
|
||||
|
||||
|
||||
def module_glue(layout, linked, hashes, paths):
|
||||
def module_glue(layout, linked, hashes, paths, version):
|
||||
"""C for build/<version>/modules.c: the layout table, the linked modules' function tables (dispatch.h), the
|
||||
expected ExeFS hashes and the default game paths (host.h)."""
|
||||
expected ExeFS hashes, the game version and the default game paths (host.h)."""
|
||||
out = [f'// Module glue written by tools/build_game.py; do not edit. {GENERATED_MARKER}',
|
||||
'#include "dispatch.h"', '#include "host.h"', '#include "layout.h"', '']
|
||||
for name, base, size in layout:
|
||||
@@ -213,7 +213,7 @@ def module_glue(layout, linked, hashes, paths):
|
||||
out += ['};', f'const int rt_exefs_sha256_count = {len(hashes)};', '']
|
||||
for name in ('exefs', 'romfs', 'profile'):
|
||||
out.append(f'const char rt_default_{name}[] = {c_string(paths[name].as_posix())};')
|
||||
out.append('')
|
||||
out += [f'const int rt_game_version = {int(DISPLAY_VERSIONS[version].replace(".", "")) if version in DISPLAY_VERSIONS else 0};', '']
|
||||
return '\n'.join(out)
|
||||
|
||||
|
||||
@@ -645,7 +645,7 @@ def main():
|
||||
missing = [n for n in names if n not in hashes]
|
||||
if missing:
|
||||
sys.exit(f'{expected}: no exefs_sha256 for {missing}')
|
||||
write_if_changed(out / 'modules.c', module_glue(layout, linked, hashes, paths))
|
||||
write_if_changed(out / 'modules.c', module_glue(layout, linked, hashes, paths, args.version))
|
||||
if (paths['romfs'] / 'index.json').is_file():
|
||||
count, size, dt = romfs_index.write(paths['romfs'], out)
|
||||
print(f'romfs index: {count} files, {mib(size)}, {dt:.1f} s')
|
||||
|
||||
Reference in new issue
Block a user