// Cheat engine unit test (hle_cheats.c): the Atmosphère opcodes against hand-computed results held to
// docs/reference/cheats.md (the memory and key conditionals, else, loops, the four load forms and the narrow load that
// keeps a register's upper bytes, the register-address store, legacy arithmetic, nested skipping, the 8-byte word order,
// a truncated instruction, 9 register arithmetic in every math type and width with the shifts and the float forms, A
// store register, C0 register conditional, C1 / C2 save and restore across cheats and their reset between passes, C3
// static registers, C4 extended key-press across passes with its one previous-keys value), the pass (registers shared
// between the cheats of one pass, masters first, reset between passes), what an access may reach (main, the heap, another
// module's data but not its code, a mapped page of the guest block but not an unmapped one) and that a failed access is
// ignored and counted, never turning the cheat off; and the host's own layer over the format: labels and EdiZon
// sections, a header glued to a code line, duplicate names in <build>.enabled, on / off pairs as one switch, the meta
// file of scripts/cheat_anchor.py (typed "ask" values kept in <build>.values, "set" cheats that run once, never-decrease,
// the compiler's left-outs, the display version), the dry run, the master switch, safe mode after an unclean exit, toggles
// carried over to a new build, DM_CHEATS_ANY_BUILD's count of build-named files, and the load report. A synthetic main
// image (text, data, key page), a second module, a heap and a guest block (one mapped page, one PROT_NONE page) live in
// host memory; writes go through the engine's real paths (the writable alias of main, vm_write for a guest page).
// Expectations are written by hand, not generated by the code under test.
#include "../hle_cheats.c"
#include <sys/stat.h>

// the loader's module table (loader.c allocates it from the NSO list and the NRO manifest), here a fixed array: "main"
// and the second modules "sdk" and "sdk2"
static module_t mods[3]; module_t *g_mod = mods; int g_nmod;
// The test's clock starts at 0 and moves 1 µs a call, so the idle thread (started by the first loaded file) always finds a present in its last 250 ms and stands down:
// every pass is the test's own. On the real clock it ran passes of its own between the test's whenever a step took longer than 250 ms (a loaded machine), and the C4
// auto-repeat counter rose once more while A was held ("C4: released: nothing fires" failed)
uint64_t now_ns(void) { static _Atomic uint64_t t; return atomic_fetch_add(&t, 1000) + 1000; }
void dm_guest_write_prepare(void *addr, size_t len) { (void)addr; (void)len; }   // no dirty-page tracking in the test
static uint8_t *heap; static size_t heap_open;   // the guest heap (hle_svc.c): a plain buffer, opened in full
void dm_guest_heap_range(uint64_t *base, size_t *open_len) { *base = (uint64_t)(uintptr_t)heap; *open_len = heap_open; }
int dm_guest_heap_read(uint64_t a, void *out, size_t n) { memcpy(out, (void *)(uintptr_t)a, n); return 0; }
int dm_guest_heap_write(uint64_t a, const void *src, size_t n) { memcpy((void *)(uintptr_t)a, src, n); return 0; }
// The reserved guest address block (hle_svc.c g_as): GAS bytes, of which only the first page is mapped (a thread stack,
// say); the second page stays PROT_NONE, an address the game has unmapped, which the engine must fail on without faulting
#define GAS 0x20000
static uint8_t *gas;
int dm_guest_as_contains(uintptr_t a) { uintptr_t b = (uintptr_t)gas; return gas && a >= b && a - b < GAS; }
static int as_ready = 1;   // 0: the block is not reserved yet (a pass before hle_svc.c's mem_init): svc_guest_range answers NULL
void svc_guest_range(uint8_t **base, size_t *size) { *base = as_ready ? gas : NULL; *size = as_ready ? GAS : 0; }
// The alias region: before the guest block is reserved none, then one laid out from the test's heap, which is not in the block (region 2's refusal)
void svc_alias_range(uint8_t **base, size_t *size) { *base = as_ready && heap ? (uint8_t *)(uintptr_t)((uint64_t)(uintptr_t)heap - DM_GAME_MEM_STACK - DM_GAME_MEM_ALIAS) : NULL; *size = as_ready ? DM_GAME_MEM_ALIAS : 0; }
static uint64_t held;                            // the keys "held" for the key-press conditional (hle_hid.c)
uint64_t dm_cheats_keys(void) { return held; }
static char moddir[1100];                        // the module folder (loader.c), with the rewriter's main.changed.tsv
const char *dm_module_dir(void) { return moddir; }

static int checks, fails;
#define CHECK(c, s) do { checks++; int ok_ = !!(c); fails += !ok_; printf("%s %s\n", ok_ ? "✓" : "✗", s); } while (0)

#define IMG 0x10000      // main: text 0x0000-0x3FFF, data 0x4000-0xBFFF, the host's key page 0xC000-0xFFFF
static uint8_t *img;     // main's image (page-aligned, so the engine can remap it)
#define SDK 0x8000       // the second module: text 0x0000-0x0FFF, read-only data 0x1000-0x1FFF, data 0x2000-0x3FFF, key page 0x4000-0x7FFF
static uint8_t *sdk;
static char root[1024];  // the test's own folder (TMPDIR); every case gets a fresh subfolder as its cheats folder

// A u32 / u16 of main's image, and setting one (the "game" changing its memory)
static uint32_t rd32(uint64_t off) { uint32_t v; memcpy(&v, img + off, 4); return v; }
static void wr32(uint64_t off, uint32_t v) { memcpy(img + off, &v, 4); }
static void wr16(uint64_t off, uint16_t v) { memcpy(img + off, &v, 2); }
static uint16_t rd16(uint64_t off) { uint16_t v; memcpy(&v, img + off, 2); return v; }

// Write a file of the cheats folder
static void put(const char *name, const char *text) {
    char p[1400]; snprintf(p, sizeof p, "%s/%s", g_dir, name);
    FILE *f = fopen(p, "w"); if (!f) { perror(p); exit(1); }
    fputs(text, f); fclose(f);
}

// Read a file of the cheats folder into buf ("" when missing), after the engine's queued writes landed (files_drain: the
// state files are written off the calling thread)
static const char *get(const char *name) {
    static char buf[8192]; buf[0] = 0;
    files_drain();
    char p[1400]; snprintf(p, sizeof p, "%s/%s", g_dir, name);
    FILE *f = fopen(p, "r"); if (!f) return buf;
    size_t n = fread(buf, 1, sizeof buf - 1, f); buf[n] = 0; fclose(f);
    return buf;
}

static int exists(const char *name) { files_drain(); char p[1400]; snprintf(p, sizeof p, "%s/%s", g_dir, name); return access(p, F_OK) == 0; }

// Remove a file of the cheats folder (the player deleting the cheat file during play)
static void rm(const char *name) { files_drain(); char p[1400]; snprintf(p, sizeof p, "%s/%s", g_dir, name); unlink(p); }

// A guest address as two code words of a 4xx load (high word first, the reference's order)
#define HI(a) ((unsigned)((uint64_t)(uintptr_t)(a) >> 32))
#define LO(a) ((unsigned)(uint64_t)(uintptr_t)(a))

// A fresh case: a new cheats folder, main's data zeroed, every engine switch back to its default; then the file is loaded
static void fresh(const char *name, const char *txt) {
    snprintf(g_dir, sizeof g_dir, "%s/%s", root, name);
    mkdir(g_dir, 0755);
    memset(img + 0x4000, 0, 0x8000);
    g_safe = 0; atomic_store(&g_master, 1); atomic_store(&g_exiting, 0); g_non = 0; held = 0;   // g_exiting: a case that exited cleanly must not mute the next one's flag writes
    if (txt) put("1111111111111111.txt", txt);
}

// The index of the entry of this name (its n-th occurrence, from 0), -1 when there is none
static int at(const char *name, int nth) {
    for (int i = 0; i < g_ncheat; i++) if (!strcmp(g_cheat[i].name, name) && nth-- == 0) return i;
    return -1;
}

// The entry of this name (its first occurrence); a missing one ends the test, which reads its fields next
static cheat_t *entry(const char *name) {
    int i = at(name, 0);
    if (i < 0) { printf("✗ no entry \"%s\"\n", name); exit(1); }
    return &g_cheat[i];
}

// Turn an entry on (toggling it when it is off) and run one pass
static void on_and_pass(int i) { if (!dm_cheat_on_at(i)) dm_cheat_toggle(i); cheats_pass(); }

int main(void) {
    const char *tmp = getenv("TMPDIR");
    snprintf(root, sizeof root, "%s/t_cheats.XXXXXX", tmp && *tmp ? tmp : "/tmp");
    if (!mkdtemp(root)) { perror("mkdtemp"); return 1; }
    img = mmap(NULL, IMG, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
    heap = calloc(1, 0x10000); heap_open = 0x10000;
    gas = mmap(NULL, GAS, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
    if (gas == MAP_FAILED || mprotect(gas, 0x10000, PROT_READ | PROT_WRITE)) { perror("guest block"); return 1; }
    sdk = mmap(NULL, SDK, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
    module_t *m = &g_mod[0]; g_nmod = 3;
    snprintf(m->name, sizeof m->name, "main"); m->base = img; m->size = IMG; m->text_end = 0x4000;
    m->key_page_off = 0xC000; m->key_page_size = 0x4000; m->patch_off = -1;
    snprintf(m->build_id, sizeof m->build_id, "1111111111111111aaaaaaaaaaaaaaaa");
    module_t *s = &g_mod[1];   // the second module's layout is the fixups header's: the engine writes only from data_off on, never the key page
    snprintf(s->name, sizeof s->name, "sdk"); s->base = sdk; s->size = SDK; s->text_end = 0x1000; s->ro_off = 0x1000; s->data_off = 0x2000;
    s->key_page_off = 0x4000; s->key_page_size = 0x4000; s->patch_off = -1;
    // A third module with its patch area after the key page (loader.c map_image, patch_off >= 0): read + execute up to
    // the image's end, so a cheat's write there must be refused, not attempted (a memcpy would be a SIGBUS)
    uint8_t *sdk2 = mmap(NULL, 0x10000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
    if (sdk2 == MAP_FAILED || mprotect(sdk2 + 0x8000, 0x8000, PROT_READ | PROT_EXEC)) { perror("sdk2"); return 1; }
    module_t *s2 = &g_mod[2];
    snprintf(s2->name, sizeof s2->name, "sdk2"); s2->base = sdk2; s2->size = 0x10000; s2->text_end = 0x1000; s2->ro_off = 0x1000; s2->data_off = 0x2000;
    s2->key_page_off = 0x4000; s2->key_page_size = 0x4000; s2->patch_off = 0x8000;
    g_main_mod = m; snprintf(g_build, sizeof g_build, "1111111111111111"); snprintf(g_tid, sizeof g_tid, "0100000000000000");
    // The rewriter's list: main+0x300..0x307 changed (an SVC site and the word after it, a window), nothing else
    snprintf(moddir, sizeof moddir, "%s/modules", root); mkdir(moddir, 0755);
    { char p[1200]; snprintf(p, sizeof p, "%s/main.changed.tsv", moddir); FILE *f = fopen(p, "w"); if (!f) { perror(p); return 1; }
      fprintf(f, "# BUILD_ID\t1111111111111111aaaaaaaaaaaaaaaa\n0x300\t0x8\n0x3000\t0x4\n"); fclose(f); }

    // ---- no cheats file: nothing is mapped and no thread is started ----
    fresh("none", NULL); load_all();
    CHECK(!g_tick_started && !g_alias && !atomic_load(&g_active), "without a cheats file no alias is mapped and no idle thread runs");

    // ---- A: store register, its six offset types and the increment ----
    char t[2048];
    uint64_t abs20 = (uint64_t)(uintptr_t)img + 0x4020;
    snprintf(t, sizeof t, "[A]\n"
             "40000000 00000000 00000063\n"                          // r0 = 0x63
             "A4000400 00004010\n"                                   // O=4: [main + 0x4010] = r0
             "40010000 %08X %08X\n"                                  // r1 = &main+0x4020 (absolute)
             "A4011000\n" "A4011000\n"                               // O=0, I=1: [r1] = r0 twice, r1 += 4 each
             "40020000 00000000 00000004\n"                          // r2 = 4
             "A4010120\n"                                            // O=1: [r1 + r2] = r0 (r1 is now +0x28: writes +0x2C)
             "A4010200 00000010\n"                                   // O=2: [r1 + 0x10] = r0 (+0x38)
             "40030000 00000000 00004040\n"                          // r3 = 0x4040
             "A4030300\n"                                            // O=3: [main + r3] = r0 (+0x40)
             "A4030500 00000004\n",                                  // O=5: [main + r3 + 4] = r0 (+0x44)
             (unsigned)(abs20 >> 32), (unsigned)abs20);
    fresh("a", t); load_all(); on_and_pass(at("A", 0));
    CHECK(rd32(0x4010) == 0x63 && rd32(0x4020) == 0x63 && rd32(0x4024) == 0x63, "A: region + address, and register with increment");
    CHECK(rd32(0x402C) == 0x63 && rd32(0x4038) == 0x63, "A: register + register, register + address");
    CHECK(rd32(0x4040) == 0x63 && rd32(0x4044) == 0x63, "A: region + register, region + register + address");

    // ---- C0: register conditionals (static value, memory, another register), then C3 static registers ----
    fresh("c0", "[C0]\n"
          // r5 holds the compared value: r0 stays 0, because opcode 0 adds its offset register (R = 0 here) to every address
          "40050000 00000000 00000005\n"                             // r5 = 5
          "C0455400 00000005\n" "04000000 00004050 00000001\n" "20000000\n"   // r5 == 5: write 1
          "C0455400 00000006\n" "04000000 00004054 00000001\n" "20000000\n"   // r5 == 6: skipped
          "C0435000 00004058\n" "04000000 0000405C 00000001\n" "20000000\n"   // r5 < [main+0x4058] (7): write
          "40060000 00000000 00000005\n"                             // r6 = 5
          "C0455560\n" "04000000 00004060 00000001\n" "20000000\n"   // r5 == r6: write
          "C3000805\n");                                             // static[0x80] = r5
    wr32(0x4058, 7);
    load_all(); on_and_pass(at("C0", 0));
    CHECK(rd32(0x4050) == 1 && rd32(0x4054) == 0, "C0: register against a static value, met and not met");
    CHECK(rd32(0x405C) == 1 && rd32(0x4060) == 1, "C0: register against memory and against another register");
    CHECK(g_static[0x80] == 5, "C3: a register written to static register 0x80");
    g_static[0x05] = 42;   // a value a tool put into a readable static register
    put("1111111111111111.txt", "[C3]\nC3000051\nA4100400 00004064\n");   // r1 = static[5]; [main+0x4064] = r1 (A: S = 1, O = 4)
    snprintf(g_path, sizeof g_path, "%s/1111111111111111.txt", g_dir);
    g_ncheat = 0; parse_file(g_path); g_active = 1; on_and_pass(at("C3", 0));   // parsed without load_all, which clears the static registers
    CHECK(rd32(0x4064) == 42, "C3: a readable static register into a register");
    load_all();
    CHECK(g_static[0x05] == 0 && g_static[0x80] == 0, "C3: loading a file clears the static registers");

    // ---- labels, EdiZon sections, a glued header, an out-of-range write ----
    fresh("labels", "[www.example.com]\n"
          "[--SectionStart:Stats--]\n"
          "[a]\n04000000 00004070 00000001[b]\n04000000 00004074 00000002\n"
          "[--SectionEnd:Stats--]\n"
          "[far]\n040000FF FFFFFF00 00000001\n");
    load_all();
    int a = at("a", 0), b = at("b", 0), credits = at("www.example.com", 0);
    CHECK(g_ncheat == 6 && b >= 0, "a header glued to the previous code line still starts an entry");
    CHECK(dm_cheat_kind_at(credits) == 3 && dm_cheat_kind_at(at("--SectionStart:Stats--", 0)) == 3 && !g_cheat[credits].broken, "entries without code are labels, not broken cheats");
    CHECK(!strcmp(dm_cheat_section_at(a), "Stats") && !strcmp(dm_cheat_section_at(b), "Stats") && !dm_cheat_section_at(at("far", 0))[0], "entries between the markers carry the section");
    CHECK((dm_cheat_flags_at(at("--SectionStart:Stats--", 0)) & DM_CHEAT_SECTION_START) && (dm_cheat_flags_at(at("--SectionEnd:Stats--", 0)) & DM_CHEAT_SECTION_END), "the markers are flagged");
    CHECK(strstr(dm_cheats_report(), "1 headers glued") != NULL, "the report counts the glued header");
    fresh("labels2", "[inline] 04000000 00004078 00000003\n[typo]\n0400000 0000407C 00000001\n[typo2]\nhello world\n[Max 999[A]]\n04000000 00004080 00000004\n");
    load_all();
    CHECK(entry("inline")[0].nins == 3 && dm_cheat_kind_at(at("inline", 0)) == 0, "code on the header's own line belongs to the entry (dmnt reads on after the header)");
    CHECK(dm_cheat_kind_at(at("typo", 0)) == 2 && strstr(dm_cheat_why_at(at("typo", 0)), "malformed line 3"), "a 7-digit word is a typo: the line is malformed, the cheat off");
    CHECK(dm_cheat_kind_at(at("typo2", 0)) == 2 && strstr(dm_cheat_why_at(at("typo2", 0)), "no code words"), "an entry whose lines were all prose is broken, not a label");
    CHECK(at("Max 999[A", 0) >= 0, "a bracket inside a name is not a glued header (the name ends at the first ], as in dmnt)");
    fresh("labels", "[www.example.com]\n"
          "[--SectionStart:Stats--]\n"
          "[a]\n04000000 00004070 00000001[b]\n04000000 00004074 00000002\n"
          "[--SectionEnd:Stats--]\n"
          "[far]\n040000FF FFFFFF00 00000001\n");
    load_all();
    on_and_pass(at("far", 0));
    CHECK(dm_cheat_kind_at(at("far", 0)) == 0 && dm_cheat_on_at(at("far", 0)) && !strcmp(dm_cheat_why_at(at("far", 0)), " (no effect: 1 access out of mapped memory)"),
          "an out-of-range write is ignored: the cheat stays on, its row carries the count");
    cheats_pass();
    CHECK(entry("far")[0].fails == 2 && strstr(dm_cheat_why_at(at("far", 0)), "2 accesses"), "the failures are counted pass after pass");
    cheats_pass();
    CHECK(entry("far")[0].fails == 3 && strstr(dm_cheat_why_at(at("far", 0)), "2 accesses"), "the note is rewritten sparingly (powers of two, or once a second): 3 still reads 2");
    cheats_pass();
    CHECK(entry("far")[0].fails == 4 && strstr(dm_cheat_why_at(at("far", 0)), "4 accesses"), "and reads 4 at the fourth");
    entry("far")[0].fails = 1;   // back to the count the dry-run check below expects
    char dry[512]; dm_cheat_preview(at("far", 0), dry, sizeof dry);
    CHECK(strstr(dry, "write at guest address") && strstr(dry, "fails") && entry("far")[0].fails == 1, "the dry run describes the failure and counts nothing");
    dm_cheat_toggle(at("far", 0));
    CHECK(!dm_cheat_why_at(at("far", 0))[0], "toggling the cheat clears the note");

    // ---- duplicate names: "Name #2" in <build>.enabled ----
    fresh("dup", "[X]\n04000000 00004080 00000001\n[X]\n04000000 00004084 00000002\n");
    load_all();
    dm_cheat_toggle(at("X", 1));
    CHECK(!strcmp(get("1111111111111111.enabled"), "X #2\n"), "the second entry of a name is saved as \"X #2\"");
    load_all();
    CHECK(!dm_cheat_on_at(at("X", 0)) && dm_cheat_on_at(at("X", 1)), "and only that entry comes back on");
    CHECK(strstr(dm_cheats_report(), "Duplicate names") != NULL, "the report names duplicate names");
    put("1111111111111111.enabled", "X\n"); load_all();
    CHECK(dm_cheat_on_at(at("X", 0)) && !dm_cheat_on_at(at("X", 1)), "a plain name (an older file) turns on the first entry");
    put("1111111111111111.enabled", "X\nX\n"); load_all();
    CHECK(dm_cheat_on_at(at("X", 0)) && dm_cheat_on_at(at("X", 1)), "an older file that names X twice keeps both on");

    // ---- an on / off pair (the naming convention) is one switch ----
    fresh("pair", "[Y - off]\n04000000 00000100 D503201F\n[Y - on]\n04000000 00000100 52800020\n");
    load_all();
    int yon = at("Y - on", 0), yoff = at("Y - off", 0);
    CHECK(dm_cheat_pair_at(yon) == yoff && (dm_cheat_flags_at(yon) & DM_CHEAT_PAIR_ON) && (dm_cheat_flags_at(yoff) & DM_CHEAT_PAIR_OFF), "\"Y - on\" / \"Y - off\" are paired");
    dm_cheat_toggle(yon); cheats_pass();
    CHECK(g_alias != NULL, "the code page write path has its writable alias of main");
    CHECK(dm_cheat_on_at(yon) && !dm_cheat_on_at(yoff) && rd32(0x100) == 0x52800020, "turning the pair on runs the on half only (code page, through the alias)");
    dm_cheat_toggle(yon);
    CHECK(!dm_cheat_on_at(yon) && dm_cheat_on_at(yoff), "turning it off turns the restore on");
    cheats_pass();
    CHECK(!dm_cheat_on_at(yoff) && rd32(0x100) == 0xD503201F && !strcmp(get("1111111111111111.enabled"), ""), "the restore runs one pass, then turns itself off");
    put("1111111111111111.enabled", "Y - off\n"); load_all();
    CHECK(dm_cheat_on_at(yoff) && !dm_cheat_on_at(yon), "a saved state is kept as it is (a hand-made file's off entry the player left on)");
    CHECK(g_tick_started && g_changed_state == 1 && g_nchanged == 2, "a loaded file starts the idle thread and reads the rewriter's list");

    // ---- code writes the rewriter's sites forbid, and the host's pages past the image ----
    wr32(0x304, 0x14000123);   // the rewriter's branch to its stub
    fresh("sites", "[Svc]\n04000000 00000304 D4000001\n[Edge]\n08000000 000002FC D503201F D503201F\n[Near]\n04000000 00000308 D503201F\n"
                   "[Key]\n04000000 0000C000 00000001\n");
    load_all();
    int svc = at("Svc", 0), edge = at("Edge", 0), near = at("Near", 0), key = at("Key", 0);
    char spv[512]; dm_cheat_preview(svc, spv, sizeof spv);
    CHECK(strstr(spv, "stops:") && strstr(spv, "rewriter changed") && rd32(0x304) == 0x14000123, "the dry run says a write into a rewritten word would stop the cheat");
    on_and_pass(svc);
    CHECK(rd32(0x304) == 0x14000123 && dm_cheat_kind_at(svc) == 2 && strstr(dm_cheat_why_at(svc), "rewriter changed"), "a cheat's original SVC over the rewriter's branch is refused, the cheat off");
    on_and_pass(edge);
    CHECK(rd32(0x300) == 0 && dm_cheat_kind_at(edge) == 2, "an 8-byte write whose second word is a changed one is refused");
    on_and_pass(near);
    CHECK(rd32(0x308) == 0xD503201F && dm_cheat_kind_at(near) == 0, "the word right after a changed run is the game's own: written");
    on_and_pass(key);
    CHECK(dm_cheat_kind_at(key) == 2 && strstr(dm_cheat_why_at(key), "past the game's image"), "a write at the key page (past the image) is refused");
    wr32(0x308, 0);
    // without a list (an older rewriter's modules), no text word can be shown safe: code writes are refused, data is not
    g_changed_state = -1;
    fresh("nolist", "[Code]\n04000000 00000308 D503201F\n[Data]\n04000000 00004300 00000005\n");
    load_all(); on_and_pass(at("Code", 0)); on_and_pass(at("Data", 0));
    CHECK(rd32(0x308) == 0 && strstr(dm_cheat_why_at(at("Code", 0)), "main.changed.tsv") && rd32(0x4300) == 5, "without the rewriter's list code writes are refused, data writes are not");
    g_changed_state = 1;

    // ---- the meta file: values, "set", never-decrease, left-outs, the version, the dry run ----
    fresh("meta", "[Gold]\n04000000 00004090 000001F4\n[HP keeps]\n[Patch]\n14050000 00000200 00000000\n04000000 00000200 52800040\n20000000\n");
    wr32(0x200, 0);
    put("1111111111111111.meta.tsv",
        "# cheat_anchor.py: compiled from s.cheats.toml for build 1111111111111111 on 2026-10-26\n"
        "# txt sha256 0000000000000000000000000000000000000000000000000000000000000000\n"
        "# build 1111111111111111 9.9.9\n"
        "name\tkind\tsection\ttype\taction\tvalue\task\tvalue_words\tonce\tpair\tresolved\tnote\taddress\n"
        "Gold\tdata\tItems\tu32\tset\t500\t0..9999\t2\t1\t\texact\t\t0:0x4090\n"
        "HP keeps\tdata\t\ts16\tnever-decrease\t\t\t\t\t\t2/2 refs (2 exact); nxnative only\t\t0:0x40A0\n"
        "Lost\tunresolved\t\t\t\t\t\t\t\t\t\tanchor s_10: its function is not in this build\t\n");
    load_all();
    int gold = at("Gold", 0), hp = at("HP keeps", 0);
    CHECK(!strcmp(dm_cheats_version(), "9.9.9") && dm_cheats_nskipped() == 1 && !strcmp(dm_cheats_skipped_name(0), "Lost"), "the version and the compiler's left-outs");
    CHECK((dm_cheat_flags_at(gold) & (DM_CHEAT_ASK | DM_CHEAT_ONCE)) == (DM_CHEAT_ASK | DM_CHEAT_ONCE) && !strcmp(dm_cheat_section_at(gold), "Items"), "Gold: ask, set once, its section");
    char v[64]; dm_cheat_value_get(gold, v, sizeof v);
    CHECK(!strcmp(v, "500") && !strcmp(dm_cheat_value_bounds(gold), "0..9999"), "its value and bounds");
    CHECK(dm_cheat_value_set(gold, "10000") == -1 && dm_cheat_value_set(gold, "-1") == -1 && dm_cheat_value_set(gold, "x") == -1, "out-of-bounds and unparsable values are refused");
    CHECK(dm_cheat_value_set(gold, "010") == 0 && g_cheat[gold].ins[2] == 10 && dm_cheat_value_set(gold, "0x10") == 0 && g_cheat[gold].ins[2] == 16, "values are decimal, hex only with 0x");
    CHECK(dm_cheat_value_set(gold, "1234") == 0 && g_cheat[gold].ins[2] == 1234 && !strcmp(get("1111111111111111.values"), "Gold\t1234\n"), "a value goes into the program and <build>.values");
    char pv[512]; int nw = dm_cheat_preview(gold, pv, sizeof pv);
    CHECK(nw == 1 && strstr(pv, "main+0x4090 (4 bytes): 0 -> 1234") && rd32(0x4090) == 0, "the dry run describes the write and writes nothing");
    on_and_pass(gold);
    CHECK(rd32(0x4090) == 1234 && !dm_cheat_on_at(gold), "a set cheat writes once and turns itself off");
    dm_cheats_master_set(0); dm_cheat_toggle(gold);
    CHECK(!strstr(get("1111111111111111.enabled"), "Gold"), "a set cheat waiting for its pass is never saved as on");
    dm_cheat_toggle(gold); dm_cheats_master_set(1);
    load_all(); dm_cheat_value_get(gold, v, sizeof v);
    CHECK(!strcmp(v, "1234"), "the value comes back from <build>.values");
    CHECK(dm_cheat_kind_at(hp) == 0 && (dm_cheat_flags_at(hp) & DM_CHEAT_HOST), "never-decrease: a cheat run by the host");
    wr16(0x40A0, 100); on_and_pass(hp);           // the first pass notes the value
    wr16(0x40A0, 50); cheats_pass();
    CHECK(rd16(0x40A0) == 100, "never-decrease: a lower value is put back");
    wr16(0x40A0, 200); cheats_pass(); wr16(0x40A0, 150); cheats_pass();
    CHECK(rd16(0x40A0) == 200, "never-decrease: it follows an increase");
    wr16(0x40A0, (uint16_t)-5); cheats_pass();
    CHECK(rd16(0x40A0) == 200, "never-decrease: a negative s16 is lower (signed compare)");
    dm_cheat_toggle(hp); dm_cheat_toggle(hp); wr16(0x40A0, 10); cheats_pass(); wr16(0x40A0, 5); cheats_pass();
    CHECK(rd16(0x40A0) == 10, "never-decrease: turning it on again starts from the value there then");
    wr32(0x200, 0x14000000);   // the "original" word changed (as a rewritten site would): the guard leaves it
    nw = dm_cheat_preview(at("Patch", 0), pv, sizeof pv);
    CHECK(nw == 0 && strstr(pv, "writes nothing right now"), "the dry run of a guard that is not met says so");

    // ---- the master switch ----
    fresh("master", "[M]\n04000000 000040B0 00000007\n");
    load_all(); dm_cheat_toggle(at("M", 0));
    dm_cheats_master_set(0); cheats_pass();
    CHECK(rd32(0x40B0) == 0 && exists(".cheats-off-0100000000000000") && strstr(dm_cheats_report(), "master switch is off"), "master switch off: nothing runs, kept in .cheats-off");
    dm_cheats_master_set(1); cheats_pass();
    CHECK(rd32(0x40B0) == 7 && !exists(".cheats-off-0100000000000000"), "master switch on again");

    // ---- safe mode after an unclean exit ----
    fresh("safe", "{Master}\n04000000 000040C0 00000001\n[S]\n04000000 000040C4 00000002\n");
    load_all(); dm_cheat_toggle(at("S", 0));
    CHECK(exists(".cheats-running-0100000000000000"), "cheats on: the safe-mode flag exists");
    folder_flags(); memset(img + 0x4000, 0, 0x100); load_all(); cheats_pass();   // the next launch, after a crash
    CHECK(dm_cheats_safe() && !dm_cheat_on_at(at("S", 0)) && rd32(0x40C0) == 0 && rd32(0x40C4) == 0, "after a crash every cheat starts off, masters too");
    CHECK(!strcmp(get("1111111111111111.enabled"), "S\n"), "and the saved toggles are kept");
    put("1111111111111111.txt", "{Master}\n04000000 000040C0 00000001\n[S]\n04000000 000040C4 00000002\n[T]\n04000000 000040C8 00000003\n");
    put("1111111111111111.enabled", "S\n"); g_safe = 1; load_all();
    dm_cheat_toggle(at("T", 0));
    CHECK(!dm_cheats_safe() && dm_cheat_on_at(at("S", 0)) && dm_cheat_on_at(at("T", 0)) && !strcmp(get("1111111111111111.enabled"), "S\nT\n"), "turning a cheat on in safe mode brings the saved toggles back too");
    put("1111111111111111.enabled", "S\n"); g_safe = 1; load_all();
    dm_cheats_safe_restore(); cheats_pass();
    CHECK(!dm_cheats_safe() && dm_cheat_on_at(at("S", 0)) && rd32(0x40C0) == 1 && rd32(0x40C4) == 2, "restoring brings them back");
    dm_cheats_exit();
    CHECK(!exists(".cheats-running-0100000000000000"), "a clean exit removes the flag");

    // ---- a compiled file of a new build takes the toggles of the previous build ----
    fresh("carry", "[Gold]\n04000000 000040D0 00000001\n[Silver]\n04000000 000040D4 00000001\n");
    put("1111111111111111.meta.tsv", "# cheat_anchor.py: compiled from s.cheats.toml for build 1111111111111111 on 2026-10-26\n"
        "name\tkind\nGold\tdata\nSilver\tdata\n");
    put("2222222222222222.enabled", "Silver\n");
    load_all();
    CHECK(!dm_cheat_on_at(at("Silver", 0)), "no toggles from a build whose file was not compiled from the same source");
    put("2222222222222222.meta.tsv", "# cheat_anchor.py: compiled from s.cheats.toml for build 2222222222222222 on 2026-10-25\nname\tkind\n");
    load_all();
    CHECK(!dm_cheat_on_at(at("Gold", 0)) && dm_cheat_on_at(at("Silver", 0)) && strstr(dm_cheats_report(), "carried over from 2222222222222222.enabled"), "toggles carried over by name");
    put("1111111111111111.enabled", ""); load_all();
    CHECK(!dm_cheat_on_at(at("Silver", 0)), "once the build has its own <build>.enabled, that one counts");

    // ---- a heap cheat before the game opens its heap waits instead of being turned off ----
    fresh("heap", "[H]\n04100000 00000010 0000002A\n");
    put("1111111111111111.enabled", "H\n");
    heap_open = 0; load_all(); cheats_pass();
    CHECK(dm_cheat_kind_at(at("H", 0)) == 0 && dm_cheat_on_at(at("H", 0)), "with no heap yet the cheat waits, still on");
    heap_open = 0x10000; cheats_pass();
    CHECK(heap[0x10] == 0x2A, "and writes once the heap is open");

    // ---- the VM held to docs/reference/cheats.md: opcodes 1 (both forms), 2 (else), nesting, 8 ----
    fresh("vm", "[Cond]\n"
          "14050000 00004200 00000007\n" "04000000 00004204 00000001\n" "20000000\n"   // [main+0x4200] == 7: met
          "40010000 00000000 00000004\n"                                             // r1 = 4
          "14051100 00004200 00000007\n" "04000000 00004208 00000001\n" "20000000\n"   // X=1, r=1: [main+0x4200 + r1] is 1: not met
          "14051100 000041FC 00000007\n" "04000000 0000420C 00000001\n" "20000000\n"   // [main+0x41FC + r1] is 7: met
          "14010000 00004200 00000007\n" "04000000 00004210 00000001\n" "21000000\n" "04000000 00004214 00000001\n" "20000000\n"   // 7 > 7: the else runs
          "14020000 00004200 00000007\n" "04000000 00004218 00000001\n" "21000000\n" "04000000 0000421C 00000001\n" "20000000\n"   // 7 >= 7: the then runs, the else is skipped
          "[Nest]\n"
          "14050000 00004200 00000000\n"                                             // [main+0x4200] == 0: not met, the whole block skipped
          "14050000 00004200 00000007\n" "04000000 00004220 00000001\n" "20000000\n" //   an inner block that would be met
          "04000000 00004224 00000001\n"
          "20000000\n"
          "04000000 00004228 00000001\n"                                             // after the outer end: runs
          "[Keys]\n" "80000003\n" "04000000 00004230 00000001\n" "20000000\n"        // A and B held, on any controller
          "[SL]\n" "81000001\n" "04000000 00004234 00000001\n" "20000000\n"          // bit 24 (a Joy-Con's SL): no input source of this host reports it
          "[Any]\n" "80000000\n" "04000000 00004238 00000001\n" "20000000\n"         // an empty mask is always met
          "[End0]\n" "20000000\n" "04000000 0000423C 00000001\n"                     // an end at depth 0 is a no-op
          "[Else0]\n" "21000000\n" "04000000 00004240 00000001\n");                  // an else at depth 0: dmnt aborts; this host turns the cheat off
    wr32(0x4200, 7);
    load_all();
    for (int i = 0; i < g_ncheat; i++) if (!dm_cheat_on_at(i) && dm_cheat_kind_at(i) == 0) dm_cheat_toggle(i);
    held = DM_BTN_A; cheats_pass();
    CHECK(rd32(0x4204) == 1 && rd32(0x4208) == 0 && rd32(0x420C) == 1, "1: the fixed form, and the register form reads at base + offset + reg");
    CHECK(rd32(0x4210) == 0 && rd32(0x4214) == 1 && rd32(0x4218) == 1 && rd32(0x421C) == 0, "2: an else runs when the if is not met, is skipped when it is");
    CHECK(rd32(0x4220) == 0 && rd32(0x4224) == 0 && rd32(0x4228) == 1, "a not-met block is skipped with its nested block, to its own end");
    CHECK(rd32(0x4230) == 0 && rd32(0x4234) == 0 && rd32(0x4238) == 1, "8: A alone does not meet A+B; an SL mask is never met; an empty mask always is");
    held = DM_BTN_A | DM_BTN_B | DM_BTN_X; cheats_pass();
    CHECK(rd32(0x4230) == 1, "8: every key of the mask held, other keys too, meets it");
    CHECK(rd32(0x423C) == 1 && rd32(0x4240) == 0 && dm_cheat_kind_at(at("Else0", 0)) == 2 && strstr(dm_cheat_why_at(at("Else0", 0)), "mismatched else"),
          "an end at depth 0 is a no-op; an else at depth 0 turns the cheat off (the lenient form of dmnt's abort)");
    held = 0;

    // ---- opcodes 3, 5 (the four forms, the narrow load), 6, 7, 0 with its register, the 8-byte word order, truncation ----
    uint64_t p4300 = (uint64_t)(uintptr_t)img + 0x4300;
    snprintf(t, sizeof t, "[P]\n40010000 00000000 00000003\n"            // r1 = 3, for Q's loop end below
             "[Q]\n78020000 00000001\n31010000\nA8200400 00004248\n"     // r2 += 1; loop end without a start: r1--, jump to Q's word 0 while r1 != 0; store r2
             "[Loop]\n30030000 00000003\n78040000 00000001\n31030000\nA8400400 00004250\n"   // r3 = 3: r4 += 1 three times; store r4
             "[Loop0]\n30050000 00000000\n31050000\n"                     // N = 0: the counter wraps, the loop practically never ends
             "[Load]\n"
             "54010000 00004300\n" "A8100400 00004320\n"                  // I=0: r1 = 4 bytes of main+0x4300 (into a zero register); stored as 8
             "40020000 FFFFFFFF FFFFFFFF\n" "54020000 00004300\n" "A8200400 00004328\n"   // a 4-byte load into a full register keeps its upper bytes
             "58030000 00004310\n" "54031000 00000004\n" "A4300400 00004330\n"   // I=1: r3 = the pointer at main+0x4310; r3 = [r3 + 4]
             "58040000 00004310\n" "54052400 00000000\n" "A4500400 00004334\n"   // I=2: r5 = [r4 + 0]
             "40060000 00000000 00004300\n" "54073600 00000004\n" "A4700400 00004338\n"   // I=3: r7 = [main + r6 + 4]
             "[Store6]\n"
             "58080000 00004310\n" "78080000 00000040\n"                  // r8 = the pointer + 0x40 (main+0x4340)
             "64081000 00000000 000000FE\n"                               // [r8] = 0xFE, r8 += 4
             "64080000 00000000 000000FD\n"                               // [r8] = 0xFD (main+0x4344)
             "40090000 00000000 00000008\n" "64080190 00000000 000000FC\n"   // [r8 + r9] = 0xFC (main+0x434C)
             "[Arith]\n"
             "40010000 00000000 00000001\n" "78013000 00000041\n" "A8100400 00004350\n"   // 1 << 65: the count is masked to 63 bits (aarch64's LSLV)
             "40020000 00000000 000000FF\n" "71020000 00000001\n" "A8200400 00004358\n"   // 0xFF + 1 at width 1: truncated to 0
             "40030000 00000000 00000005\n" "74031000 00000007\n" "A8300400 00004360\n"   // 5 - 7 at width 4: 0xFFFFFFFE, truncated to 4 bytes
             "40040000 00000000 00000006\n" "78042000 00000007\n" "78044000 00000001\n" "A8400400 00004368\n"   // 6 * 7 >> 1 = 21
             "[Words]\n" "08000000 00004370 11111111 22222222\n"          // an 8-byte value: first word high, second low
             "40010000 00000000 00000008\n" "04010000 00004378 00000005\n"   // 0 adds its register: [main+0x4378 + r1]
             "[Trunc]\n04000000 00004000\n"                               // the value word is missing
             "[Trunc2]\n04000000 00004390 00000001\n40010000 00000000\n"); // a good store, then a 4 cut short: the cheat never runs here
    fresh("vm2", t);
    wr32(0x4300, 0xAABBCCDD); wr32(0x4304, 0x11223344); memcpy(img + 0x4310, &p4300, 8);
    load_all();
    for (int i = 0; i < g_ncheat; i++) if (!dm_cheat_on_at(i) && dm_cheat_kind_at(i) == 0) dm_cheat_toggle(i);
    cheats_pass();
    CHECK(rd32(0x4248) == 3 && rd32(0x424C) == 0, "3: a loop end without its start jumps to the first word of its own cheat (r1 from the cheat before, shared in the pass)");
    CHECK(rd32(0x4250) == 3, "3: the body of a loop of 3 runs three times");
    CHECK(dm_cheat_kind_at(at("Loop0", 0)) == 2 && strstr(dm_cheat_why_at(at("Loop0", 0)), "runaway"), "3: a loop of 0 never ends on a Switch; here the step cap turns it off");
    CHECK(rd32(0x4320) == 0xAABBCCDD && rd32(0x4324) == 0, "5: the fixed form, 4 bytes into a zero register");
    CHECK(rd32(0x4328) == 0xAABBCCDD && rd32(0x432C) == 0xFFFFFFFF, "5: a load narrower than 8 bytes keeps the register's upper bytes");
    CHECK(rd32(0x4330) == 0x11223344 && rd32(0x4334) == 0xAABBCCDD && rd32(0x4338) == 0x11223344, "5: reg + offset, other reg + offset, base + reg + offset");
    CHECK(rd32(0x4340) == 0xFE && rd32(0x4344) == 0xFD && rd32(0x434C) == 0xFC, "6: the register address, the increment, the offset register");
    CHECK(rd32(0x4350) == 2 && rd32(0x4354) == 0, "7: a shift count of 65 shifts by 1 (the host masks it; the reference leaves it undefined)");
    CHECK(rd32(0x4358) == 0 && rd32(0x435C) == 0 && rd32(0x4360) == 0xFFFFFFFE && rd32(0x4364) == 0 && rd32(0x4368) == 21, "7: the result is truncated to the width; mul and shr");
    CHECK(rd32(0x4370) == 0x22222222 && rd32(0x4374) == 0x11111111, "0: an 8-byte value is two words, high then low, stored little-endian");
    CHECK(rd32(0x4380) == 5 && rd32(0x4378) == 0, "0: the offset register is always added");
    CHECK(dm_cheat_kind_at(at("Trunc", 0)) == 2 && strstr(dm_cheat_why_at(at("Trunc", 0)), "truncated"), "an instruction whose last word is missing marks the cheat at load");
    CHECK(dm_cheat_kind_at(at("Trunc2", 0)) == 2 && rd32(0x4390) == 0, "a cheat cut short never runs here (the reference runs its whole words, then ends the pass)");
    {   // a preview of every cheat leaves the cheats and the game's memory as they were
        static uint8_t before[0x8000]; static cheat_t keepc[CHEAT_MAX];
        memcpy(before, img + 0x4000, 0x8000); memcpy(keepc, g_cheat, sizeof(cheat_t) * (size_t)g_ncheat);
        char pvx[512]; for (int i = 0; i < g_ncheat; i++) dm_cheat_preview(i, pvx, sizeof pvx);
        int same = !memcmp(before, img + 0x4000, 0x8000);
        for (int i = 0; i < g_ncheat && same; i++)
            same = keepc[i].broken == g_cheat[i].broken && keepc[i].dead == g_cheat[i].dead && keepc[i].on == g_cheat[i].on
                && keepc[i].fails == g_cheat[i].fails && !strcmp(keepc[i].why, g_cheat[i].why);
        CHECK(same, "a preview of every cheat changes no flag, no reason and no memory");
        cheat_t probe; memset(&probe, 0, sizeof probe); uint32_t badw = 0x04000000; ins_t inx;   // a store cut short
        g_dry = 1; int r = ins_dec(&probe, &badw, 1, &inx); g_dry = 0;
        CHECK(r == 0 && !probe.broken && !probe.why[0] && !strcmp(g_dec_why, "truncated"), "a decode failure under the dry flag describes itself and marks nothing");
    }
    // loop tops are per cheat: L1's loop on r1 leaves no top for L2's stray loop end, which jumps to L2's own word 0
    fresh("tops", "[L1]\n30010000 00000002\n04000000 00004400 00000001\n31010000\n40010000 00000000 00000003\n"   // loop of 2 on r1 (top at word 2); then r1 = 3
          "[L2]\n78020000 00000001\n31010000\nA8200400 00004410\n");                                            // r2 += 1; stray end on r1; store r2
    load_all(); dm_cheat_toggle(at("L1", 0)); dm_cheat_toggle(at("L2", 0)); cheats_pass();
    CHECK(rd32(0x4400) == 1 && rd32(0x4410) == 3 && dm_cheat_kind_at(at("L2", 0)) == 0, "a stray loop end jumps to word 0 of its own cheat, never to a top another cheat set");
    // a note from a failing pass, then a kill in a pass without a failure: the kill reason stays
    snprintf(t, sizeof t, "[K]\n58010000 000044B0\n58011000 00000000\n64010000 00000000 00000001\n");   // r1 = the pointer at main+0x44B0; r1 = [r1]; [r1] = 1
    fresh("kill", t); load_all(); dm_cheat_toggle(at("K", 0)); cheats_pass();
    CHECK(strstr(dm_cheat_why_at(at("K", 0)), "no effect"), "the chase fails while the pointer is 0: noted");
    { uint64_t p = (uint64_t)(uintptr_t)img + 0x4500, k = (uint64_t)(uintptr_t)img + 0xC000; memcpy(img + 0x44B0, &p, 8); memcpy(img + 0x4500, &k, 8); }
    cheats_pass();   // the pointer now leads to the host's key page: the write is refused for good
    CHECK(entry("K")[0].dead && dm_cheat_kind_at(at("K", 0)) == 2 && strstr(dm_cheat_why_at(at("K", 0)), "key page"), "a kill after a noted pass keeps its reason");
    // a module whose patch area follows its key page (read + execute to the image's end): a write there is refused, one into its data lands
    { uint64_t pp = (uint64_t)(uintptr_t)g_mod[2].base + 0x8000, pd = (uint64_t)(uintptr_t)g_mod[2].base + 0x2000;
      snprintf(t, sizeof t, "[Patch]\n40010000 %08X %08X\n64010000 00000000 00000001\n40020000 %08X %08X\n64020000 00000000 00000005\n", HI(pp), LO(pp), HI(pd), LO(pd)); }
    fresh("patcharea", t); load_all(); dm_cheat_toggle(at("Patch", 0)); cheats_pass();
    { uint32_t gd; memcpy(&gd, g_mod[2].base + 0x2000, 4);
      CHECK(entry("Patch")[0].fails == 1 && gd == 5, "a write into another module's patch area is refused (counted), one into its data lands"); }
    // region 2 (alias) before the guest block is reserved: the cheat waits silently; afterwards a base outside the block is refused once
    fresh("alias", "[Al]\n04200000 00000010 00000001\n");   // M = 2
    load_all(); dm_cheat_toggle(at("Al", 0));
    as_ready = 0; cheats_pass(); as_ready = 1;
    CHECK(entry("Al")[0].fails == 0 && dm_cheat_kind_at(at("Al", 0)) == 0, "region 2 before mem_init: not ready, nothing counted, nothing logged");
    cheats_pass();   // the test's heap is not the block's partition: a real mismatch, logged once, the cheat stays on
    CHECK(entry("Al")[0].fails == 0 && dm_cheat_kind_at(at("Al", 0)) == 0, "region 2 with a base outside the block: refused, the cheat stays on");
    // glued hex words are split as dmnt does; a hex token that is not whole words makes the line malformed
    fresh("glued", "[G]\n0400000000004420 00000003\n04000000 0000442400000004\n[Odd]\n0400000 00004428 00000001\n[Odd2]\n04000000 00004428\n0400000000004428 0000000\n[Prose]\nby dead beef\n04000000 0000442C 00000005\n");
    load_all();
    for (int i = 0; i < g_ncheat; i++) if (!dm_cheat_on_at(i) && dm_cheat_kind_at(i) == 0) dm_cheat_toggle(i);
    cheats_pass();
    CHECK(entry("G")[0].nins == 6 && rd32(0x4420) == 3 && rd32(0x4424) == 4, "a run of hex digits is cut into 8-digit words, glued or not");
    CHECK(dm_cheat_kind_at(at("Odd", 0)) == 2 && strstr(dm_cheat_why_at(at("Odd", 0)), "malformed line 5"), "a 7-digit hex token makes its line malformed, not prose");
    CHECK(dm_cheat_kind_at(at("Odd2", 0)) == 2 && strstr(dm_cheat_why_at(at("Odd2", 0)), "malformed line 8"), "a glued run of 23 digits too");
    CHECK(dm_cheat_kind_at(at("Prose", 0)) == 2 && strstr(dm_cheat_why_at(at("Prose", 0)), "malformed line 10") && rd32(0x442C) == 0, "a line of short hex-looking words (\"dead beef\") is malformed, so the cheat stays off");

    // ---- failed accesses: ignored and counted; what a read or write can reach ----
    uint64_t pmapped = (uint64_t)(uintptr_t)gas + 0x100, pnone = (uint64_t)(uintptr_t)gas + 0x10000, psdk = (uint64_t)(uintptr_t)sdk + 0x100, psdkd = (uint64_t)(uintptr_t)sdk + 0x2000;
    snprintf(t, sizeof t, "[Fail]\n40010000 00000000 00000010\n58011000 00000000\nA8100400 000043A0\n"   // r1 = 0x10; r1 = [r1]: unmapped; store r1
             "[Ptr]\n58010000 000043B0\n54011000 00000000\nA4100400 000043B8\n"                       // r1 = the pointer at main+0x43B0 (0 at first); r1 = [r1]; store
             "[FarW]\n040000FF FFFFFF00 00000001\n04000000 000043C0 00000001\n"                       // a write nothing maps, then a good one
             "[Stack]\n40010000 %08X %08X\n54011000 00000000\nA4100400 000043C8\n"                    // a read of a mapped page of the guest block
             "40020000 %08X %08X\n54021000 00000000\nA8200400 000043D0\n"                             // a read of its PROT_NONE page: the register keeps the address
             "40030000 %08X %08X\n64030000 00000000 00000077\n"                                       // a write into the mapped page
             "40040000 %08X %08X\n64040000 00000000 00000077\n"                                       // a write into the PROT_NONE page: nothing, no fault
             "[Module]\n40010000 %08X %08X\n54011000 00000000\nA4100400 000043D8\n"                   // a read of another module's code
             "40020000 %08X %08X\n64020000 00000000 00000001\n"                                       // a write into its code: refused
             "40030000 %08X %08X\n64030000 00000000 00000002\n",                                      // a write into its data: done
             HI(pmapped), LO(pmapped), HI(pnone), LO(pnone), HI(pmapped + 0x100), LO(pmapped + 0x100), HI(pnone), LO(pnone),
             HI(psdk), LO(psdk), HI(psdk), LO(psdk), HI(psdkd), LO(psdkd));
    fresh("fail", t);
    wr32(0x4300, 0xAABBCCDD);
    memset(gas, 0, 0x10000); wr32(0x43B0, 0); wr32(0x43B4, 0);
    uint32_t v5a = 0x5A5A; memcpy(gas + 0x100, &v5a, 4);
    uint32_t v33 = 0x33; memcpy(sdk + 0x100, &v33, 4); memset(sdk + 0x2000, 0, 4);
    load_all();
    for (int i = 0; i < g_ncheat; i++) if (!dm_cheat_on_at(i) && dm_cheat_kind_at(i) == 0) dm_cheat_toggle(i);
    cheats_pass();
    int fl = at("Fail", 0), ptr = at("Ptr", 0), farw = at("FarW", 0), stk = at("Stack", 0), mod = at("Module", 0);
    CHECK(rd32(0x43A0) == 0x10 && rd32(0x43A4) == 0 && dm_cheat_kind_at(fl) == 0 && dm_cheat_on_at(fl) && g_cheat[fl].fails == 1, "a failed load leaves the register as it was; the cheat stays on, the failure counted");
    CHECK(rd32(0x43B8) == 0 && entry("Ptr")[0].fails == 1 && strstr(dm_cheat_why_at(ptr), "no effect"), "a pointer the game has not set yet: the chase fails, the cheat waits with a note");
    CHECK(rd32(0x43C0) == 1 && g_cheat[farw].fails == 1 && dm_cheat_on_at(farw), "a failed write is a no-op; the next store of the cheat runs");
    CHECK(rd32(0x43C8) == 0x5A5A, "a read of a mapped page of the guest block (a stack, virtual address memory)");
    CHECK(rd32(0x43D0) == (uint32_t)pnone && rd32(0x43D4) == (uint32_t)(pnone >> 32), "a read of an unmapped guest page fails without a fault and keeps the register");
    uint32_t got77; memcpy(&got77, gas + 0x200, 4);
    CHECK(got77 == 0x77 && g_cheat[stk].fails == 2, "a write into a mapped guest page lands; one into an unmapped page fails without a fault (2 failures)");
    uint32_t gotd; memcpy(&gotd, sdk + 0x2000, 4); uint32_t gotc; memcpy(&gotc, sdk + 0x100, 4);
    CHECK(rd32(0x43D8) == 0x33 && gotc == 0x33 && gotd == 2 && g_cheat[mod].fails == 1, "another module: its code is readable, never written; its data is written");
    memcpy(img + 0x43B0, &p4300, 8);   // the game sets the pointer
    cheats_pass();
    CHECK(rd32(0x43B8) == 0xAABBCCDD && !dm_cheat_why_at(ptr)[0] && entry("Ptr")[0].fails == 1, "once the pointer is set the chase works and the note goes");
    char pv2[512]; dm_cheat_preview(fl, pv2, sizeof pv2);
    CHECK(strstr(pv2, "read at guest address 0x10 fails") && fl >= 0 && g_cheat[fl].fails == 2, "the dry run describes a failed read and counts nothing");

    // ---- registers: shared by the cheats of one pass (masters first), reset between passes; the dry run replays the pass ----
    fresh("regs", "[P2]\n40010000 00000000 00000005\n"   // r1 = 5
          "[Q2]\nA4100400 000043E0\n"                     // store r1
          "[Q3]\nA4200400 000043E8\n"                     // store r2, set by the master below it in the file
          "{M2}\n40020000 00000000 00000009\n");          // r2 = 9: the master runs first whatever its place
    load_all();
    dm_cheat_toggle(at("P2", 0)); dm_cheat_toggle(at("Q2", 0)); dm_cheat_toggle(at("Q3", 0));
    cheats_pass();
    CHECK(rd32(0x43E0) == 5 && rd32(0x43E8) == 9, "a register set by an earlier cheat, or by the master, is seen by a later one in the same pass");
    char pv3[512]; dm_cheat_preview(at("Q2", 0), pv3, sizeof pv3);
    CHECK(strstr(pv3, "main+0x43E0 (4 bytes): 5 (0x5) -> 5 (0x5)") != NULL, "the dry run replays the cheats before it, so its registers are a real pass's");
    dm_cheat_toggle(at("P2", 0)); wr32(0x43E0, 7); cheats_pass();
    CHECK(rd32(0x43E0) == 0, "the registers are reset at the start of every pass");

    // ---- opcode 9: every math type at every width, the immediate forms, the shifts, the float forms; the load verdict ----
    fresh("math", "[Math]\n"
          "40010000 00000000 FFFFFFFF\n" "94021100 00000002\n" "A8200400 00004600\n"     // r2 = (0xFFFFFFFF + 2) at width 4: 1
          "40010000 00000000 00000005\n" "91131100 00000107\n" "A8300400 00004608\n"     // r3 = (5 - 7) at width 1: 0xFE; the width-1 immediate is the word's low byte
          "92021100 00012345\n" "A8200400 00004698\n"                                    // r2 = 5 + 0x2345 at width 2 (the immediate's low 16 bits): 0x234A
          "40040000 00000000 00001234\n" "40050000 00000000 00000010\n" "92264050\n" "A8600400 00004610\n"   // r6 = r4 * r5 at width 2: 0x12340 cut to 0x2340
          "98371100 00000000 00000041\n" "A8700400 00004618\n"                           // r7 = 5 << 65: the count is masked to 63 bits (aarch64's LSLV), 10
          "98381100 00000000 00000004\n" "A8800400 00004620\n"                           // r8 = 5 << 4 = 0x50
          "94498100 00000004\n" "A8900400 00004628\n"                                    // r9 = r8 >> 4 = 5
          "400A0000 00000000 000000F0\n" "400B0000 00000000 0000003C\n"
          "915CA0B0\n" "A8C00400 00004630\n"                                             // rC = rA & rB = 0x30
          "916DA0B0\n" "A8D00400 00004638\n"                                             // rD = rA | rB = 0xFC
          "927EA000\n" "A8E00400 00004640\n"                                             // rE = ~rA at width 2 = 0xFF0F
          "948FA0B0\n" "A8F00400 00004648\n"                                             // rF = rA ^ rB = 0xCC
          "9891A000\n" "A8100400 00004650\n"                                             // r1 = rA (move) = 0xF0
          "94E1A000\n" "A8100400 00004658\n"                                             // an unknown math type (14) stores 0
          "40010000 80000000 00000000\n" "98411100 00000000 0000003F\n" "A8100400 00004690\n"   // the top bit >> 63 at width 8: 1
          "[Float]\n"
          "40010000 FFFFFFFF 3F800000\n" "94A21100 40000000\n" "A8200400 00004660\n"     // 1.0f (the register's upper half ignored) + 2.0f = 3.0f
          "40030000 3FF00000 00000000\n" "98B43100 40080000 00000000\n" "A8400400 00004668\n"   // 1.0 - 3.0 = -2.0 (double)
          "40050000 00000000 40400000\n" "94C65050\n" "A8600400 00004670\n"              // 3.0f * 3.0f (register operand) = 9.0f
          "40070000 40100000 00000000\n" "98D83070\n" "A8800400 00004678\n"              // 1.0 / 4.0 (register operand) = 0.25
          "40090000 00000000 00000001\n" "92A91100 00003C00\n" "A8900400 00004680\n"     // a float add at width 2 gives 0
          "94DA5100 00000000\n" "A8A00400 00004688\n"                                    // 3.0f / 0.0f = +inf, no trap
          "[T9]\n98011100 00000001\n"                                                    // an 8-byte immediate cut to one word
          "[Log]\nFFF40400\n");                                                          // the debug log stays unsupported
    load_all();
    for (int i = 0; i < g_ncheat; i++) if (!dm_cheat_on_at(i) && dm_cheat_kind_at(i) == 0) dm_cheat_toggle(i);
    cheats_pass();
    CHECK(rd32(0x4600) == 1 && rd32(0x4604) == 0 && rd32(0x4608) == 0xFE && rd32(0x4698) == 0x234A, "9: add at width 4 and sub at width 1 truncate the result; a narrow immediate is the word's low bytes");
    CHECK(rd32(0x4610) == 0x2340 && rd32(0x4614) == 0, "9: mul of two registers at width 2");
    CHECK(rd32(0x4618) == 10 && rd32(0x461C) == 0 && rd32(0x4620) == 0x50 && rd32(0x4628) == 5, "9: shl by 65 shifts by 1 (the count masked as the Switch's core does), shl by 4, shr by 4");
    CHECK(rd32(0x4690) == 1 && rd32(0x4694) == 0, "9: shr by 63 of a full 64-bit register");
    CHECK(rd32(0x4630) == 0x30 && rd32(0x4638) == 0xFC && rd32(0x4640) == 0xFF0F && rd32(0x4648) == 0xCC, "9: and, or, not (at width 2), xor");
    CHECK(rd32(0x4650) == 0xF0 && rd32(0x4658) == 0, "9: move; an unknown math type stores 0");
    CHECK(rd32(0x4660) == 0x40400000 && rd32(0x4664) == 0, "9: float add at width 4 on the register's low 32 bits");
    CHECK(rd32(0x4668) == 0 && rd32(0x466C) == 0xC0000000, "9: float sub at width 8 (-2.0)");
    CHECK(rd32(0x4670) == 0x41100000 && rd32(0x4678) == 0 && rd32(0x467C) == 0x3FD00000, "9: float mul (9.0f) and div (0.25) with register operands");
    CHECK(rd32(0x4680) == 0 && rd32(0x4684) == 0 && rd32(0x4688) == 0x7F800000, "9: a float operation at width 2 gives 0; a division by zero gives an infinity");
    CHECK(dm_cheat_kind_at(at("T9", 0)) == 2 && strstr(dm_cheat_why_at(at("T9", 0)), "truncated"), "9: an immediate cut short marks the cheat at load");
    CHECK(dm_cheat_kind_at(at("Log", 0)) == 2 && strstr(dm_cheat_why_at(at("Log", 0)), "unsupported opcode FFF"), "the debug log is still an unsupported opcode");
    CHECK(value_word(entry("Math"), 4) && !value_word(entry("Math"), 3), "9: its immediate word is a value word the panel may set, its opcode word is not");

    // ---- C1 / C2: save and restore across the cheats of a pass, every operation; the saved values reset between passes ----
    fresh("save", "[Save]\n"
          "40010000 00000000 00000005\n" "40020000 00000000 00000006\n"   // r1 = 5, r2 = 6
          "C1030110\n"                                                     // saved[3] = r1
          "C2100004\n"                                                     // mask save: saved[2] = r2
          "[Restore]\n"
          "C1040300\n" "A8400400 00004700\n"                               // r4 = saved[3]: 5, from the cheat before
          "C1020030\n" "A8200400 00004708\n"                               // clear r2: 0
          "C2000004\n" "A8200400 00004710\n"                               // mask restore: r2 = saved[2]: 6
          "40050000 00000000 00000007\n" "C1030020\n" "C1050300\n" "A8500400 00004718\n"   // clear saved[3]; r5 = saved[3]: 0
          "40060000 00000000 00000009\n" "C2200004\n" "C1060200\n" "A8600400 00004720\n"   // mask clear saved[2]; r6 = saved[2]: 0
          "C2300010\n" "A8400400 00004728\n"                               // mask clear r4: 0
          "C1030110\n" "C1070390\n" "A8700400 00004730\n");                // saved[3] = r1 again; an unknown x (9) restores: r7 = 5
    load_all(); dm_cheat_toggle(at("Save", 0)); dm_cheat_toggle(at("Restore", 0)); cheats_pass();
    CHECK(rd32(0x4700) == 5 && rd32(0x4704) == 0 && rd32(0x4710) == 6, "C1 / C2: a value saved by one cheat is restored by a later one of the same pass");
    CHECK(rd32(0x4708) == 0 && rd32(0x4718) == 0 && rd32(0x4720) == 0 && rd32(0x4728) == 0, "C1 / C2: clear register, clear saved value, by index and by mask");
    CHECK(rd32(0x4730) == 5, "C1: an unknown operation restores");
    dm_cheat_toggle(at("Save", 0)); cheats_pass();
    CHECK(rd32(0x4700) == 0 && rd32(0x4710) == 0, "the saved values are reset at the start of every pass, like the registers");

    // ---- C4 across passes: the edge form fires on the pass where the keys became held, against one previous-keys value shared by every C4 ----
    fresh("c4", "[Edge]\n"  "C4000000 00000000 00000001\n" "54010000 00004740\n" "78010000 00000001\n" "A4100400 00004740\n" "20000000\n"   // r = 0, A: the counter at 0x4740 + 1
          "[Edge2]\n" "C4000000 00000000 00000001\n" "54010000 00004760\n" "78010000 00000001\n" "A4100400 00004760\n" "20000000\n"   // the same mask again
          "[Repeat]\n" "C4100000 00000000 00000001\n" "54010000 00004748\n" "78010000 00000001\n" "A4100400 00004748\n" "20000000\n"  // r = 1: while held
          "[High]\n" "C4100000 00000001 00000000\n" "04000000 00004750 00000001\n" "20000000\n"      // bit 32 of the 64-bit mask: no input source reports it
          "[Empty0]\n" "C4000000 00000000 00000000\n" "04000000 00004758 00000001\n" "20000000\n");  // an empty mask without auto-repeat: never an edge
    load_all();
    for (int i = 0; i < g_ncheat; i++) if (!dm_cheat_on_at(i) && dm_cheat_kind_at(i) == 0) dm_cheat_toggle(i);
    held = DM_BTN_A; cheats_pass();
    CHECK(rd32(0x4740) == 1 && rd32(0x4760) == 1 && rd32(0x4748) == 1, "C4: A pressed: the edge form and the auto-repeat form both fire; two edge C4s of one mask both see the edge");
    cheats_pass();
    CHECK(rd32(0x4740) == 1 && rd32(0x4760) == 1 && rd32(0x4748) == 2, "C4: A still held: only the auto-repeat form fires again");
    held = 0; cheats_pass();
    CHECK(rd32(0x4740) == 1 && rd32(0x4748) == 2, "C4: released: nothing fires");
    held = DM_BTN_A | DM_BTN_B; cheats_pass();
    CHECK(rd32(0x4740) == 2 && rd32(0x4760) == 2 && rd32(0x4748) == 3, "C4: pressed again, with another key too: a new edge");
    CHECK(rd32(0x4750) == 0 && rd32(0x4758) == 0, "C4: a mask above bit 23 is never held; an empty mask is never an edge");
    held = 0; cheats_pass(); held = DM_BTN_A;
    char pv4[512]; int nw4 = dm_cheat_preview(at("Edge", 0), pv4, sizeof pv4);
    CHECK(nw4 == 1 && strstr(pv4, "main+0x4740 (4 bytes): 2 (0x2) -> 3 (0x3)"), "C4: the dry run sees the edge");
    cheats_pass();
    CHECK(rd32(0x4740) == 3, "C4: the dry run did not consume it: the next pass fires");
    held = 0;

    // ---- DM_CHEATS_ANY_BUILD counts only files named after a build ID; the running flag follows a reload that finds no file ----
    fresh("any", NULL); put("readme.txt", "hello\n");
    g_any = 1; load_all();
    CHECK(!dm_cheats_found() && g_ncheat == 0, "ANY_BUILD: a lone readme.txt is not a cheat file of another build");
    put("2222222222222222.txt", "[Z]\n04000000 000043F0 00000001\n"); load_all();
    CHECK(dm_cheats_found() && at("Z", 0) >= 0 && strstr(g_path, "2222222222222222.txt"), "ANY_BUILD: the single build-named file loads, the readme beside it ignored");
    g_any = 0;
    fresh("nofile", "[R]\n04000000 000043F8 00000001\n");
    load_all(); dm_cheat_toggle(at("R", 0));
    CHECK(exists(".cheats-running-0100000000000000"), "a cheat on: the safe-mode flag is there");
    rm("1111111111111111.txt"); load_all();
    CHECK(!dm_cheats_found() && !exists(".cheats-running-0100000000000000"), "a reload that finds no file removes the flag: nothing runs now");

    // ---- Atmosphère's limits in the report ----
    size_t cap = 0x200 * 9 + 64; char *big = malloc(cap); size_t o = (size_t)snprintf(big, cap, "[Big]\n");
    for (int k = 0; k < 0x200; k++) o += (size_t)snprintf(big + o, cap - o, "20000000\n");
    fresh("limits", big); free(big);
    load_all();
    CHECK(strstr(dm_cheats_report(), "1 cheats over 0x100 words") != NULL, "the report names a cheat over Atmosphère's 0x100 words");

    char rmcmd[1100]; snprintf(rmcmd, sizeof rmcmd, "rm -rf '%s'", root);   // the test's cheats folders
    if (system(rmcmd)) fprintf(stderr, "cannot remove %s\n", root);   // NOLINT(bugprone-command-processor): a fixed rm of the folder mkdtemp made above, no outside input
    printf("%d / %d checks passed\n", checks - fails, checks);
    return fails != 0;
}
