#!/usr/bin/env python3 """Does mission time advance at all? Scan for a linearly-increasing counter. Six runs have shown no arrival, and one surviving explanation is that the mission's phase clock is simply not running under these conditions (mission-arrival-watch.md). That has never been checked. If nothing in the game heap advances linearly, the clock is stopped and every arrival result so far is measuring a stopped clock. If something does, that explanation is dead and a timer has been located as a bonus. Three snapshots, equally spaced. A counter is a word whose two successive deltas are both positive and agree within a tolerance -- i.e. it is linear, not just noisy -- at a plausible rate. """ import os, sys, time, struct, collections sys.path.insert(0, __file__.rsplit('/', 1)[0]) import gmem, gworld LO, HI = 0xBC000000, 0xBE000000 # the heap holding roster records + craft def snap(fd): lo, hi = gmem.va_to_off(LO), gmem.va_to_off(HI) out, pos = bytearray(), lo while pos < hi: n = min(1 << 24, hi - pos) out += os.pread(fd, n, pos) pos += n return bytes(out), lo def main(): dt = int(sys.argv[1]) if len(sys.argv) > 1 else 20 w = gworld.World(); fd = w.fd print('scanning %#x..%#x, %ds apart' % (LO, HI, dt), flush=True) a, base = snap(fd); ta = time.time() time.sleep(dt) b, _ = snap(fd); tb = time.time() time.sleep(dt) c, _ = snap(fd); tc = time.time() d1, d2 = tb - ta, tc - tb print('actual gaps: %.1fs, %.1fs region %.1f MB' % (d1, d2, len(a) / 2**20)) n = min(len(a), len(b), len(c)) cand = [] for k in range(0, n - 3, 4): va = struct.unpack_from('>I', a, k)[0] vb = struct.unpack_from('>I', b, k)[0] vc = struct.unpack_from('>I', c, k)[0] if not (va < vb < vc): continue e1, e2 = vb - va, vc - vb if e1 > 1 << 28 or e2 > 1 << 28: continue r1, r2 = e1 / d1, e2 / d2 if r1 < 0.4 or r1 > 200: continue if abs(r1 - r2) > 0.12 * max(r1, r2): continue # linear, not noisy cand.append((k, va, vb, vc, (r1 + r2) / 2)) print('\nlinearly-increasing words: %d' % len(cand)) if not cand: print('NONE -- nothing in this heap advances at a steady rate.') return 0 rates = collections.Counter(round(r[4]) for r in cand) print('rate histogram (per second):', rates.most_common(10)) print('\nsample candidates:') for k, va, vb, vc, r in cand[:14]: gva = gmem.primary_va(base + k) print(' va %s %10d -> %10d -> %10d %.1f/s' % (('%#010x' % gva) if gva else '?', va, vb, vc, r)) return 0 if __name__ == '__main__': sys.exit(main())