"""Independent check of the live ledger: pull the RNG blob out of two save files and compute the word delta from the FILES, with no reference to the shim's numbers.""" import sys, os sys.path.insert(0, os.path.expanduser('~/sots-re/verify/save-reader')) import save_reader as sr N, M = 624, 397 def twist(mt): mt = list(mt) for kk in range(N - M): y = (mt[kk] & 0x80000000) | (mt[kk+1] & 0x7fffffff) mt[kk] = mt[kk+M] ^ (y >> 1) ^ (0x9908b0df if y & 1 else 0) for kk in range(N - M, N - 1): y = (mt[kk] & 0x80000000) | (mt[kk+1] & 0x7fffffff) mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ (0x9908b0df if y & 1 else 0) y = (mt[N-1] & 0x80000000) | (mt[0] & 0x7fffffff) mt[N-1] = mt[M-1] ^ (y >> 1) ^ (0x9908b0df if y & 1 else 0) return mt def find_rng(node, out): name = getattr(node, 'name', None) if name == 'RNG': out.append(node) for c in getattr(node, 'children', []) or []: find_rng(c, out) def rng_of(path): res = sr.read_save(path) hits = [] find_rng(res.tree, hits) if not hits: raise SystemExit(f"no RNG frame in {path}") n = hits[0] raw = n.raw if not raw and n.children: raw = b"".join(c.raw for c in n.children) if not raw: raise SystemExit(f"RNG frame in {path} carries no bytes") return raw def parse(raw): # the blob is mt[624] then left, little-endian; tolerate a leading/trailing frame byte for off in range(0, len(raw) - 2500 + 1): if len(raw) - off < 2500: continue mt = [int.from_bytes(raw[off+4*i:off+4*i+4], 'little') for i in range(N)] left = int.from_bytes(raw[off+2496:off+2500], 'little', signed=True) if 0 <= left <= N: return mt, left, off, len(raw) raise SystemExit(f"cannot parse RNG blob of {len(raw)} bytes") a, b = sys.argv[1], sys.argv[2] ra, rb = rng_of(a), rng_of(b) ma, la, oa, na = parse(ra) mb, lb, ob, nb = parse(rb) print(f"{os.path.basename(a)}: blob {na} B, offset {oa}, left={la}") print(f"{os.path.basename(b)}: blob {nb} B, offset {ob}, left={lb}") cur, tw = ma, 0 while tw <= 64 and cur != mb: cur = twist(cur); tw += 1 if cur != mb: raise SystemExit("second block is not on the first block's chain within 64 twists") words = 624*tw + (la - lb) print(f"twists={tw} words consumed between the two files = {words}")