Field notes · 2026-08-13

Inside AutoHotkey64.exe: Runtime Scanning, In-Process Eval, and Remote Introspection

How an AutoHotkey v2 script reads the interpreter that is running it, rebuilds embedded x64 machine code, evaluates expressions in-process, loads script text from memory, and inspects remote AHK processes.

Repository: MonoEven/ahk-hack-library

Objective and method

The goal is to analyze AutoHotkey64.exe as a live interpreter and extract enough structure to implement expression evaluation, script loading from memory, remote hooking, and live function tracing. The method is a repeatable loop: read the source tree to identify candidate fields, inspect process memory to confirm their actual offsets, encode the stable logic as position-independent machine code, then validate every result against structural invariants.

This page documents that loop in detail.

PE parsing and section mapping

Every x64 AutoHotkey v2 executable starts with an MZ DOS header. The PE signature is at the offset stored in e_lfanew. The optional header uses PE32+ magic 0x20B. The section table immediately follows the optional header.

peOff   := NumGet(base, 0x3C, "UInt")
numSec  := NumGet(base + peOff + 6, "UShort")
optSize := NumGet(base + peOff + 20, "UShort")
secTab  := base + peOff + 24 + optSize

loop numSec {
    off := (A_Index - 1) * 40
    name := StrGet(hdr.Ptr + off, 8, "UTF-8")
    vsize := NumGet(hdr + off + 8, "UInt")
    va := NumGet(hdr + off + 12, "UInt")
    rawSize := NumGet(hdr + off + 16, "UInt")
}

The scanner stores each section as {rva, size, ptr} and classifies it by name. Three sections matter:

SectionRoleInvariant
.textexecutable codefunction pointers must land here
.rdataread-only stringsUTF-16 names usually live here
.datawritable globalsinterpreter tables live here

For packed builds, section names are unreliable. The scanner falls back to content-based classification: non-.rsrc code-like sections are treated as executable, and readable non-executable sections are treated as data/string storage.

Interpreter table discovery

The interpreter keeps three tables in the image. Their record strides are stable on x64:

TableContentsRecord stride
g_BIFscript-visible built-ins such as Abs and Sin0x20
sMdFunctyped native functions such as MsgBox0x28
g_BIV_Abuilt-in variables beginning with A_0x18

The scanner does not hardcode addresses. It walks pointer-aligned slots and accepts a record only when:

  • the first qword resolves to a printable UTF-16 string;
  • the second qword resolves into .text;
  • neighboring records are sorted by name;
  • a known anchor is present, such as Abs, BlockInput, or AhkPath.

The longest valid run becomes the table. The same algorithm runs as x64 machine code, so the scan speed and the scan logic do not depend on the AHK version.

Source-to-memory validation

Source code gives names and intent, not final offsets. The compiler, build flags, and debugger fields move members. The workflow therefore uses source to form hypotheses and memory to confirm them.

For UserFunc in script.h, the source declares:

class UserFunc : public Func
{
public:
    int mInstances = 0;
    Line *mJumpToLine = nullptr;
    FuncParam *mParam = nullptr;
    Object *mClass = nullptr;
    ...
    VarList mVars {};
    VarList mStaticVars {};
};

The memory probe confirms the actual x64 layout on two representative builds:

Fieldv2.1-alpha.30v2.0.26
mName0x280x20
mJumpToLine0x480x40
mParam0x500x48
mVars0x800x70

The offsets differ, so the library discovers them at runtime and does not maintain a version table.

Machine-code extraction pipeline

The stable scanner is written in C under lib/mcode/. The build uses clang to produce a freestanding, position-independent COFF object:

clang -c -O2 -target x86_64-pc-windows-msvc -ffreestanding \
  -fno-builtin -fno-stack-protector -fno-unwind-tables \
  -fno-asynchronous-unwind-tables -fno-jump-tables scanner.c

tools/build_mcode.py performs four checks:

  • extract .text from the COFF object;
  • decode every relocation record;
  • accept IMAGE_REL_AMD64_REL32 only when the target stays inside .text;
  • reject any blob with an escaping relocation.

The final hex string is embedded into ahk_hack_single.ahk. MCode() decodes it into a Buffer, changes protection to executable, and returns it for DllCall.

EvalNative: expression evaluation analysis

AutoHotkey does not evaluate expression strings directly. During script load, Line::ExpressionToPostfix compiles an expression into an ExprTokenType postfix array. Line::ExpandExpression later evaluates that array.

EvalNative() constructs the same temporary Line and ArgStruct the parser would have built, then calls those two functions. This reuses the interpreter's own grammar and variable resolution.

The internal locator machine code discovers:

SymbolSourceDiscovery
ExpressionToPostfixerror string "Missing operand."RIP-relative ref + prologue walk
ExpandExpressionerror string "Error evaluating expression."RIP-relative ref + prologue walk
gScriptmost frequent lea rcx,[rip+disp] targetfrequency filter
FindOrAddVarsix-argument prologuesignature match
SYM_INVALIDpostfix terminatorimmediate inside evaluator

The token layout used by the evaluator is:

FieldOffsetType
value0x00pointer/int64/double union
marker/usage0x08pointer or VarRefUsageType
symbol0x1032-bit SymbolType
stride0x18total struct size
#Include ahk_hack_single.ahk
AhkMagic.Init()

MsgBox AhkMagic.EvalNative("1 + 2 * 3")           ; 7
MsgBox AhkMagic.EvalNative("2 * 3.5")             ; 7.0
MsgBox AhkMagic.EvalNative("Abs(-5)")             ; 5
MsgBox AhkMagic.EvalNative("SubStr(`"abc`", 2)")  ; "bc"

myVar := 42
MsgBox AhkMagic.EvalNative("myVar + 1")           ; 43

EvalScript: parser and in-memory loading

Expression eval cannot define functions or classes. EvalScript() feeds multi-line text through the interpreter's include pipeline from an in-memory TextStream.

The relevant pipeline is:

ResultType Script::LoadIncludedFile(TextStream *fp)
{
    int source_file_index = mCurrFileIndex;
    bool blocks_previously_open = mLineParent || mClassObjectCount;
    auto module_previously_open = mCurrentModule;
    bool caller_backcompatmode = mBackCompatMode;
    ...
}

Before loading, the library saves and later restores:

  • mOpenBlock / mLineParent;
  • mPendingRelatedLine;
  • mLastParamInitializer;
  • mPendingHotkey;
  • mExprFuncIndex;
  • mNextLineIsFunctionBody;
  • mIgnoreNextBlockBegin;
  • mBackCompatMode;
  • mClassObjectCount;
  • mCurrentModule and mLastModule.

Parser-state anchors are read from the LoadIncludedFile prologue:

  • q: the offset of mOpenBlock/mLineParent;
  • d: the offset of mClassObjectCount.

The TextStream object is built with both possible mData regions populated, so the same object works for 2.0 and 2.1 layouts; every load is validated by its result (rc == 1 plus a function-count delta for the probe).

Remote introspection

AttachRemote(pid) opens the target with OpenProcess, reads its PE image, scans its interpreter tables, and returns a hook map.

Remote eval injects a thread stub plus the locator and evaluator blobs:

StepOperation
1allocate a scratch block with VirtualAllocEx
2write stub, locator, evaluator, expression, output buffers
3create a remote thread with CreateRemoteThread
4wait and read the result buffer
5free the scratch block

RemoteEvalScript() uses the target's own LoadIncludedFile and PreparseExpressions. The injected thread sets g->CurrentFunc before preparse, so class methods and Super parse correctly.

hook := AhkMagic.AttachRemote(pid)
MsgBox AhkMagic.RemoteEval(hook, "1 + 2 * 3")     ; 7

script := "
(
rhkAdd(a, b) {
    return a + b
}
rhkAdd(1, 2)
)"
MsgBox AhkMagic.RemoteEvalScript(hook, script)    ; 3

Function body replacement swaps mJumpToLine:

AhkMagic.RemoteEvalScript(hook, "
(
NewA() {
    return 42
}
NewA()
)")
AhkMagic.RemoteReplaceFuncBody(hook, "A", "NewA")
Send("{F9}")

The target writes A()=42 after the original output was A()=1.

Dynamic offset discovery

The remote layout scanner derives mFuncs, mFuncsCount, the function name field, and mJumpToLine from live memory:

  • candidate array/count pairs are validated by reading each Func pointer;
  • mName is a pointer to a valid identifier string;
  • mJumpToLine points to a Line that contains a back-reference to its owning Func;
  • the back-reference slot is consistent across ordinary functions.

The struct validator then discovers:

StructValidated fields
Lineaction, argc, arg pointer, attribute, next line
ArgStructis_expression, postfix pointer, deref pointer
ExprTokenTypestride, symbol offset, usage offset, value offset
DerefTypemarker, var, type, length

Token arrays are validated by walking until SYM_INVALID and requiring a SYM_VAR token before the terminator. DerefType is read from ArgStruct.deref arrays that already exist in the running process.

TraceFunction: clone-based tracing

Function tracing is harder than function replacement. The original implementation renamed the target function and injected a wrapper. That broke because:

  • function names live in Func.mName and global Var.mName;
  • mFuncs and VarList are sorted arrays;
  • renaming one side breaks binary search;
  • body replacement alone does not rebind parameter Var objects.

The stable fix reads UserFunc::Call:

bool UserFunc::Call(ResultToken &aResultToken, ExprTokenType *aParam[], int aParamCount)
{
    ...
    for (j = 0; j < mParamCount; ++j)
    {
        FuncParam &this_formal_param = mParam[j];
        ...
    }
}

The call path writes through mParam[i].var. Therefore two function objects can share the same parameter Var objects, and both bodies see the same values.

The final design:

  • clone the UserFunc object;
  • create a VAR_CONSTANT global alias for the clone;
  • inject a wrapper that calls the clone;
  • alias the wrapper's parameter Var objects to the original parameters;
  • replace the original function body pointer;
  • on untrace, restore the original mJumpToLine.

The wrapper logs to a target-side global string, so RemoteEval can read the trace without relying on file I/O inside the evaluator thread.

Verification matrix

tools/verify_all_runtimes.ps1 runs thirteen tests against every runtime found on this machine:

TestCovers
ahk_live_cross_smokeattach, snapshot, inventory, replace, watch
ahk_live_testfull trace/replace path
ahk_live_product_testsession and patch rollback
ahk_live_six_testobservability, fault, desktop, forensics, agent, CSV
ahk_live_benchmarkeval/snapshot/list timings
remote_hook_testremote eval, script, class, function replacement
evalscript_inprocin-process script loading
evalscript_repeatrepeated function/class loading
evalscript_classclass definitions
eval_object_syntax_testobject return values and syntax-error survival in-process
remote_empty_object_testremote object results and syntax-error survival
remote_empty_target_testprobe-injection retry on targets with no user functions
remote_deep_redirect_testdeep redirect through direct and dynamic call chains

Current result:

Runtime familyBuildsResult
2.0 betabeta.9, beta.10, beta.12, beta.13, beta.1513/13 each
2.0 rcrc.1, rc.313/13 each
2.0 stable0.0, 0.2, 0.3, 0.4, 0.2613/13 each
2.1 alphaalpha.1, alpha.4, alpha.13, alpha.16, alpha.3013/13 each
local dev buildstwo 2.0-beta binaries13/13 each

Total: 19 runtimes, all 13/13.

11.1 Regression coverage: error capture

The GUI converts every AhkLiveResult into a visible log line. OnError() appends exception fields to both the GUI log and %TEMP%\ahk_live_gui_error.log; SafeStr() serializes Map and Array values so a second error cannot break the logger. Watch and observability errors are forwarded to the callback as Error objects. --shot selects a tab and writes a ready marker for full-window screenshot automation.

AhkLive_OnError(e, exitCode := 0) {
    msg := "ERR " e.What " | " e.Message
        . " | " e.File ":" e.Line
    FileAppend(msg "`n", A_Temp "\ahk_live_gui_error.log")
    return true
}

OnError(AhkLive_OnError)

The regression test tests\ahk_live_regression.ahk covers three failure modes found while building the GUI:

  • EvalScript followed by TraceFunction, which requires the cached script_layout to backfill find_var from script_loc;
  • snapshot expressions that contain commas inside parentheses, which must not be split as spec separators;
  • duplicate top-level function names, which are rejected before LoadIncludedFile runs:
for name in AhkMagic._DeclaredFunctionNames(text) {
    if AhkMagic._RemoteFindUserFuncs(h, layout, name).Length
        throw Error("function name already exists in target: " name, -1)
}

The new regression tests pass on 2.1-alpha.30 and 2.0.26. The 19-runtime thirteen-test matrix — now including the four robustness regressions above — remains the full compatibility gate, and all 19 runtimes pass 13/13.

Security and boundaries

This library allocates executable memory, executes machine code, and writes interpreter memory in the current or target process. It is not a sandbox.

Rules:

  • only attach to processes you own;
  • use disposable targets for repeated experiments;
  • restore patches in finally or through AhkLivePatchSession.Rollback();
  • never eval untrusted input;
  • treat locator failures as errors, never as a reason to guess.

Packed or hostile builds can rename sections or remove strings. The content-based scanner handles common packers, but a hostile build can still defeat discovery.

Failure behavior

Every path that reads interpreter memory or applies a patch is designed to fail explicitly:

AreaBehavior
Table and export scanscounts are capped against the scanner contract and every name pointer is memory-map checked before use; unsupported layouts throw instead of reading out of bounds
Syntax errorsEvalScript/RemoteEvalScript pre-check the text with the interpreter's own /validate (the target's executable for the remote path); the validator runs hidden, captures its output (no dialogs), and kills its process tree on timeout
Remote timeoutsa hung evaluation throws after 10 s and leaks its injected block, because freeing memory the target is still executing would corrupt it
Object resultsEvalNative returns live Func/Class objects; RemoteEval returns the target-side object address
Empty targetsRemoteEvalScript injects randomized probe functions and retries layout discovery when the target has no user functions
PatchesPatchBifObject and RemoteDeepRedirect commit atomically with rollback; the discovery probe restores its interpreter snapshot in finally

Further reading

Appendix: single-file core

The copy below is generated from the repository file by the build script.

ahk_hack_single.ahk
; ahk-hack standalone core.
;
; One-file AutoHotkey v2 scanner for AutoHotkey executables.  Copy this file
; anywhere and #Include it, or run ahk_hack_demo.ahk for a self-test.
; The x64 blob below is generated from lib/mcode/scanner.c by
; tools/build_mcode.py; it locates g_BIF, sMdFunc and g_BIV_A inside the
; running interpreter and copies entry metadata into a caller buffer.
MC_REMOTE_CALL_STUB_X64 := "564883ec304889ce0f1041284c8b49204c8b4118488b4908488b56100f11442420ff1689463831c04883c4305ec3"
MC_REMOTE_EVAL_STUB_X64 := "564881ec800000004889ce0f1041304c8b49284c8b4120488b4910488b56180f11442420ff1689c1894678b80100000085c9755b488b4638488b4e400f10000f104810488b40204c8b96800000000f1056604c8b4e584c8b4650488b56484c8954247048894424680f114c24580f114424480f57c00f114424380f11542420c744243001000000ff560889467c31c04881c4800000005ec3"
MC_PE_EXPORT_SCANNER_X64 := "415741564155415456575553504885c90f94c04885d2410f94c04108c0b8010000000f85f00000006681394d5a0f85e5000000448b413c41813c08504500000f85d30000006641817c08180b020f85c5000000418bb40888000000458b8c088c0000004885f6410f94c24585c9410f94c34508d34180fb0175190f57c00f1102be100000004531c94c890c3231c0e985000000458b4408504d85c0747b4439c673764589c94d89ca4901f24d39c2410f97c24183f928410f92c14508d17559448b740e18448b540e144d85d2410f94c14585f6410f95c34584d9753c4181fe0010000041b900100000450f42ce4c8d3c0e458b5f204b8d3c8b4c39c7771a8b7c0e244a8d1c4f4c39c3770d8b5c0e1c4e8d24934d39c476114883c4085b5d5f5e415c415d415e415fc38b740e10897424044c893a48c7421000000000be080000004585f60f843bffffff4801cb4901cb4801cf4e8d34cd000000004f8d34764531ff458b234539c473ae440fb72f4539ea76a5428b2cab4439c5739c4901cc4e89643a184a896c3a2044036c240446896c3a2842c7443a2c000000004983c3044883c7024983c7184d39fe75b5e9d6feffff"
MC_BIF_SCANNER_X64 := "4157415641554154565755534881ec780200004885c90f94c04885d2410f94c04108c0be010000000f85060700006681394d5a0f85fb0600008b413c813c08504500000f85eb06000066817c08180b020f85de060000440fb7540806664183fa1041b910000000450f42ca448b5c08504d8d040b4d85db4d0f44c3664585d20f8400030000440fb7540814410fb7f94801c84c01d0448d0cfd000000004f8d0c894c8d5424704531dbbb010000004531f631edeb4c660f7f442450440fb67c2452440fb66424534180f7614180f4744508fc440fb67c24544180f761400fb6ed4508e70f44eb662e0f1f8400000000004983c3284983c2204d39d90f84ed000000468b7c18204585ff460f447c1828468b6418244901cc4d01e74d39c74d89c54d0f42ef4d85c04d0f44ef4d89224d896a084e8b7c181866490f6ec7660f7f442460440fb66424604d897a134180fc2e759e66410f7ec741c1ef084180ff640f8450ffffff450fb6ff4183ff7274414183ff740f8577ffffff660f7f442430440fb67c2432440fb66424334180f7654180f4784508fc440fb67c24344180f774450fb6f64508e7440f44f3e940ffffff660f7f442440807c2442640f852fffffff807c2443610f8524ffffff807c2444740f8519ffffff66440fc5f80241c1ef084180ff61400fb6ede9f5feffff4584f60f95c14084ed0f95c020c889f9c1e1054531c0eb2d662e0f1f8400000000006642c784048000000000004531c946888c04820000004983c0204c39c10f8454010000460fb69404830000004180fa2e753e4280bc04840000007275434280bc04850000007375384280bc048600000072752d4280bc048700000063410f94c184c0746ceb21662e0f1f84000000000084c0758c4531c9eb570f1f80000000004531c984c074494180fa2e0f856fffffff4280bc048400000074754d4280bc04850000006575424280bc04860000007875374280bc048700000074410f94c1eb2b6666666666662e0f1f8400000000004180f10146888c048000000046888c0481000000e925ffffff4531c946888c04800000004280bc04840000007275304280bc04850000006475254280bc048600000061751a4280bc048700000074750f4280bc048800000061410f94c1eb034531c946888c04810000004280bc04840000006475284280bc048500000061751d4280bc04860000007475124280bc048700000061410f94c1e9a1feffff4531c9e999feffff31ff488d420848894424284989d74889542420488d4c247041b92000000089fa4531c0e8a303000085c00f847f0300004d8d7720498d47184c897424284889442420488d4c2470be0100000041b92800000089fa41b801000000e86c03000085c00f8448030000498d5f38498d473048895c24284889442420488d4c247041b91800000089fa41b802000000e83a03000085c00f84160300004c89f849c747102000000049c747282800000049c747401800000049c7474800000000418b570885d27e6dffca4881faff010000b9ff010000480f42ca488b10c1e1054883c1204531c00f1f8400000000004e8b0c024e894c00504e8b4c02084e894c0058460fb64c021046894c0060460fb64c021146894c0064460fb64c021246894c006842c744006c000000004983c0204c39c175ba418b0e85c90f8ea5010000ffc94881f9ff010000baff010000480f42d1488b4818488d1492488d14d5280000004531c0662e0f1f8400000000004e8b0c014e898c00504000004e8b4c01084e898c0058400000460fb64c011046888c0060400000460fb64c011146888c0061400000460fb64c011246888c0062400000460fb64c011346888c0063400000460fb64c011446888c0064400000460fb64c011546888c0065400000460fb64c011646888c0066400000460fb64c011746888c0067400000460fb64c011846888c0068400000460fb64c011946888c0069400000460fb64c011a46888c006a400000460fb64c011b46888c006b400000460fb64c011c46888c006c400000460fb64c011d46888c006d400000460fb64c011e46888c006e400000460fb64c011f46888c006f400000460fb64c012046888c0070400000460fb64c012146888c0071400000460fb64c012246888c0072400000460fb64c012346888c0073400000460fb64c012446888c0074400000460fb64c012546888c0075400000460fb64c012646888c0076400000460fb64c012746888c00774000004983c0284c39c20f858afeffff448b034585c00f8e8c00000041ffc84181f8ff000000baff000000410f42d0488b4830ffc231f64585c074704189d14181e1fe0100004531d24531c04e8b1c114e899c10509000004e8b5c11084e899c10589000004e8b5c11104e899c10609000004e8b5c11184e899c10689000004e8b5c11204e899c10709000004e8b5c11284e899c10789000004983c0024983c2304d39c175a6eb0731f6eb324531c0f6c201742a48055090000049c1e0034b8d14404c8b04114c8904104c8b4411084c89441008488b4c111048894c101089f04881c4780200005b5d5f5e415c415d415e415fc3660f1f4400004157415641554154565755534883ec3885d20f8e270500004c894c241889d64889f748c1e70531db41be4000000048c74424200000000048c744242800000000eb1a66666666662e0f1f84000000000048ffc34839f30f84c80400004889da48c1e205488d0411807c11110075068078120074dc4c8b204c8b7808498d4424104c39f877cb4d8b2c24eb2e4c8b0c24488b4424284939c1490f47c14889442428488b442420480f47c5488944242066904883c5184c39fd77974c89e54c89e84983c4084c8b6d084531d2eb0d0f1f40004983c2204c39d774d742807c111100750842807c11120074e74e8b4c11084929c176dd4a39041177d74983f90272b149d1e94983f9404d0f43ce49ffc94989c2450fb71a664585db741f4183c3814983e9010f92c2664183fba172844983c20284d274dce977ffffff4531c9eb0f66904983c1204c39cf0f8463ffffff42807c09100074eb4e396c090876e44e392c0977de0fb7104183f8010f84de0200004585c00f854d0300006683fa410f852effffff66837802620f8523ffffff41b90600000066837804730f8512ffffff6641833c01000f8506ffffff488b4424184801e84883c01041b9010000004c39f80f87cafeffff488b4424184801e84889ea48895424104889c24c8b10488b400848894424084531dbeb140f1f80000000004983c3204c39df0f8492feffff42807c191100750842807c19120074e34a8b4419084c29d076d94e39141977d34883f8020f8268feffff48895424304c890c2448d1e84883f840490f43c648ffc84d89d36666666666662e0f1f840000000000410fb7136685d2742383c2814883e801410f92c16683faa10f821dfeffff4983c3024584c974d9e90ffeffff31c04c8b0c24488b542408eb140f1f80000000004883c0204839c70f84f2fdffff807c01100074ec483954010876e54839140177df4183f80275406641833a590f85620100006641837a02590f85560100006641837a04590f854a0100006641837a06590f853e0100006641837a08000f8532010000e925020000488b442410488b104531c9eb094983c1204c39cf742542807c091100750842807c09120074e74a8b4409084829d076dd4a39140977d748d1e8eb0231c04531c9eb094983c1204c39cf745542807c091100750842807c09120074e74e8b5c09084d29d376dd4e39140977d74d89d949d1e94889542408488d50ff4883fa3f488b542408490f43c64939c14c0f43c84983fb024c0f42c84c894c24104d85c9eb704989d1488d50ff4883fa3f4c89ca490f43c648894424104c8b0c240fb702450fb71a6685c07451664585db744b4889542408418d53bf458d4b2080fa1a410fb6d1410f43d3448d48bf448d58204180f91a450fb6cb440f43c84138d175254983c202488b5424084883c20248ff4c24104c8b0c2475a5eb15664439d8760fe975fcffff4c8b0c240f836bfcffff49ffc1488b5424184c8b542430498d04124c01d24883c2104c39fa4c89d20f8688fdffffe942fcffff6683fa420f8559fcffff668378026c0f854efcffff668378046f0f8543fcffff66837806630f8538fcffff668378086b0f852dfcffff6683780a490f8522fcffff6683780c6e0f8517fcffff6683780e700f850cfcffff66837810750f8501fcffff41b91400000066837812740f85f0fbffffe9d9fcffff6683fa410f85e1fbffff66837802680f85d6fbffff668378046b0f85cbfbffff66837806500f85c0fbffff66837808610f85b5fbffff6683780a740f85aafbffff41b90e0000006683780c680f8599fbffffe982fcffff49ffc1e96bfbffff4c8b4424204d85c00f94c0488b5424284883fa0a0f92c108c1740431c0eb1b488b8424a00000004c8900488b8424a8000000488910b8010000004883c4385b5d5f5e415c415d415e415fc3"


; Decode a raw hex machine-code blob and return an executable Buffer.
MCode(hex) {
    size := StrLen(hex) // 2
    buf := Buffer(size)
    loop size {
        byte := Integer("0x" SubStr(hex, 2 * A_Index - 1, 2))
        NumPut("UChar", byte, buf, A_Index - 1)
    }
    oldProtect := 0
    if !DllCall("VirtualProtect", "Ptr", buf.Ptr, "UPtr", size, "UInt", 0x40, "UInt*", oldProtect)
        throw Error("VirtualProtect failed", -1)
    return buf
}


class AhkMagic {
    static scanner := 0
    static exportScanner := 0
    static inprocEval := 0
    static memScriptLoader := 0
    static internalLocator := 0
    static internalLocated := false
    static evalScriptLocated := false
    static evalLayoutLocated := false
    static evalMFuncsOff := 0
    static evalMFuncsCountOff := 0
    static evalMLastLineOff := 0
    static evalMJumpLineOff := 0
    static evalCurrOff := 0
    static evalStructOffsets := 0
    static evalLayoutArr := 0
    static evalParserLocated := false
    static evalParser := Map()
    static evalPreparse := 0
    static evalPreprocess := 0
    static evalOpenInclude := 0
    static evalLoadTs := 0
    static evalSrcCount := 0
    static evalGptr := 0
    static exprToPostfix := 0
    static expandSingleArg := 0
    static currLineSlot := 0
    static crtFree := 0
    static gScript := 0
    static finalizeExpr := 0
    static findOrAddVar := 0
    static symInvalid := 73
    static moduleBase := 0
    static bifTablePtr := 0
    static bifCount := 0
    static bifStride := 0
    static mdfuncTablePtr := 0
    static mdfuncCount := 0
    static mdfuncStride := 0
    static bivTablePtr := 0
    static bivCount := 0
    static bivStride := 0
    static bif := Map()
    static mdfunc := Map()
    static biv := Map()

    static Init() {
        if AhkMagic.scanner
            return
        if A_PtrSize != 8
            throw Error("this mcode blob is x64 only", -1)
        try {
            AhkMagic._InitTables()
        } catch as e {
            ; A half-initialized state would make later calls fail with
            ; misleading errors; reset so Init can be retried cleanly.
            AhkMagic.scanner := 0
            AhkMagic.bif.Clear()
            AhkMagic.mdfunc.Clear()
            AhkMagic.biv.Clear()
            throw e
        }
    }

    ; Parse the scanner output into bif/mdfunc/biv maps.  The scanner copies
    ; at most 512/512/256 entries into the 64 KiB output buffer; larger
    ; tables are an explicit unsupported-layout error, never a silent read.
    static _InitTables() {
        AhkMagic.scanner := MCode(MC_BIF_SCANNER_X64)
        AhkMagic.moduleBase := DllCall("GetModuleHandle", "Str", A_AhkPath, "Ptr")
        if !AhkMagic.moduleBase
            throw Error("GetModuleHandle failed", -1)

        out := Buffer(64 * 1024)
        rc := DllCall(AhkMagic.scanner.Ptr, "Ptr", AhkMagic.moduleBase, "Ptr", out.Ptr, "Int")
        if rc != 0
            throw Error("AhkScanTables failed with rc=" rc, -1)

        AhkMagic.bifTablePtr := NumGet(out, 0, "Ptr")
        AhkMagic.bifCount := NumGet(out, 8, "Int64")
        AhkMagic.bifStride := NumGet(out, 16, "Int64")
        AhkMagic.mdfuncTablePtr := NumGet(out, 24, "Ptr")
        AhkMagic.mdfuncCount := NumGet(out, 32, "Int64")
        AhkMagic.mdfuncStride := NumGet(out, 40, "Int64")
        AhkMagic.bivTablePtr := NumGet(out, 48, "Ptr")
        AhkMagic.bivCount := NumGet(out, 56, "Int64")
        AhkMagic.bivStride := NumGet(out, 64, "Int64")

        ; The copy loops below run on raw pointer arithmetic without any
        ; buffer object, so validate every count and stride BEFORE looping.
        if AhkMagic.bifCount < 1 or AhkMagic.bifCount > 512
            throw Error("g_BIF table count out of scanner contract: "
                . AhkMagic.bifCount, -1)
        if AhkMagic.mdfuncCount < 1 or AhkMagic.mdfuncCount > 512
            throw Error("sMdFunc table count out of scanner contract: "
                . AhkMagic.mdfuncCount, -1)
        if AhkMagic.bivCount < 1 or AhkMagic.bivCount > 256
            throw Error("g_BIV_A table count out of scanner contract: "
                . AhkMagic.bivCount, -1)
        if AhkMagic.bifStride != 0x20 or AhkMagic.mdfuncStride != 0x28
            or AhkMagic.bivStride != 0x18
            throw Error("unexpected interpreter table strides", -1)

        bifBase := 80
        bifStride := 32
        loop AhkMagic.bifCount {
            p := out.Ptr + bifBase + (A_Index - 1) * bifStride
            namePtr := NumGet(p, 0, "Ptr")
            fnPtr := NumGet(p, 8, "Ptr")
            min := NumGet(p, 16, "UInt")
            max := NumGet(p, 20, "UInt")
            fid := NumGet(p, 24, "UInt")
            if !AhkMagic._Readable(namePtr, 8)
                throw Error("g_BIF entry " A_Index " has an invalid name pointer", -1)
            name := StrGet(namePtr, 256, "UTF-16")
            AhkMagic.bif[name] := Map(
                "index", A_Index - 1,
                "rva", fnPtr - AhkMagic.moduleBase,
                "min", min,
                "max", max,
                "fid", fid
            )
        }

        mdfBase := 80 + 512 * 32
        mdfStride := 40
        loop AhkMagic.mdfuncCount {
            p := out.Ptr + mdfBase + (A_Index - 1) * mdfStride
            namePtr := NumGet(p, 0, "Ptr")
            fnPtr := NumGet(p, 8, "Ptr")
            retType := NumGet(p, 16, "UChar")
            if !AhkMagic._Readable(namePtr, 8)
                throw Error("sMdFunc entry " A_Index " has an invalid name pointer", -1)
            name := StrGet(namePtr, 256, "UTF-16")
            AhkMagic.mdfunc[name] := Map(
                "index", A_Index - 1,
                "rva", fnPtr - AhkMagic.moduleBase,
                "ret", retType
            )
        }

        bivBase := 80 + 512 * 32 + 512 * 40
        bivStride := 24
        loop AhkMagic.bivCount {
            p := out.Ptr + bivBase + (A_Index - 1) * bivStride
            namePtr := NumGet(p, 0, "Ptr")
            getter := NumGet(p, 8, "Ptr")
            setter := NumGet(p, 16, "Ptr")
            if !AhkMagic._Readable(namePtr, 8)
                throw Error("g_BIV_A entry " A_Index " has an invalid name pointer", -1)
            name := StrGet(namePtr, 256, "UTF-16")
            AhkMagic.biv[name] := Map(
                "index", A_Index - 1,
                "getter_rva", getter ? getter - AhkMagic.moduleBase : 0,
                "setter_rva", setter ? setter - AhkMagic.moduleBase : 0
            )
        }
    }

    ; True when [ptr, ptr+size) falls inside committed readable memory.
    ; Used before dereferencing pointers that come from scanned tables.
    static _Readable(ptr, size) {
        if !ptr or ptr >= 0x7FFFFFFFFFFF
            return false
        mbi := Buffer(48)
        if !DllCall("VirtualQuery", "Ptr", ptr, "Ptr", mbi.Ptr, "UPtr", 48)
            return false
        state := NumGet(mbi, 32, "UInt")
        protect := NumGet(mbi, 36, "UInt")
        if state != 0x1000 ; MEM_COMMIT
            return false
        if protect & 0x100 ; PAGE_GUARD
            return false
        readable := (protect & 0x02) or (protect & 0x04) or (protect & 0x08)
            or (protect & 0x20) or (protect & 0x40) or (protect & 0x80)
        if !readable
            return false
        regionBase := NumGet(mbi, 0, "Ptr")
        regionSize := NumGet(mbi, 24, "Ptr")
        return ptr >= regionBase and ptr + size <= regionBase + regionSize
    }

    static BifRva(name) {
        AhkMagic.Init()
        if !AhkMagic.bif.Has(name)
            throw Error("builtin not found: " name)
        return AhkMagic.bif[name]["rva"]
    }

    static BifAddr(name) {
        AhkMagic.Init()
        return AhkMagic.moduleBase + AhkMagic.BifRva(name)
    }

    static PatchBif(name, newName) {
        AhkMagic.Init()
        if !AhkMagic.bif.Has(name)
            throw Error("builtin not found: " name)
        if !AhkMagic.bif.Has(newName)
            throw Error("builtin not found: " newName)
        entryAddr := AhkMagic.bifTablePtr
            + AhkMagic.bif[name]["index"] * AhkMagic.bifStride
            + A_PtrSize
        oldPtr := NumGet(entryAddr, 0, "Ptr")
        expected := AhkMagic.moduleBase + AhkMagic.bif[name]["rva"]
        if oldPtr != expected
            throw Error("table pointer mismatch for " name
                ": got 0x" Format("{:X}", oldPtr)
                ", expected 0x" Format("{:X}", expected))
        NumPut("Ptr", AhkMagic.moduleBase + AhkMagic.bif[newName]["rva"], entryAddr)
        return oldPtr
    }

    static RestoreBif(name, oldPtr) {
        AhkMagic.Init()
        if !AhkMagic.bif.Has(name)
            throw Error("builtin not found: " name)
        entryAddr := AhkMagic.bifTablePtr
            + AhkMagic.bif[name]["index"] * AhkMagic.bifStride
            + A_PtrSize
        NumPut("Ptr", oldPtr, entryAddr)
    }

    ; Deep patch: change the mBIF field of an already-resolved built-in Func
    ; object, so even direct calls compiled at load time (e.g. Abs(1)) are
    ; redirected.  Works together with PatchBif on the table itself.
    static PatchBifObject(fnObj, newName) {
        AhkMagic.Init()
        if !(fnObj is Func)
            throw TypeError("expected a Func object")
        if !AhkMagic.bif.Has(newName)
            throw Error("builtin not found: " newName)
        fnName := fnObj.Name
        if !AhkMagic.bif.Has(fnName)
            throw Error("function object is not a built-in: " fnName)
        oldAddr := AhkMagic.moduleBase + AhkMagic.bif[fnName]["rva"]
        newAddr := AhkMagic.moduleBase + AhkMagic.bif[newName]["rva"]
        rawPtr := ObjPtr(fnObj)
        off := AhkMagic._FindBifPtrOffset(rawPtr, oldAddr)
        if off < 0
            throw Error("could not locate mBIF in function object for " fnName)
        ; Patch the table first, then the object.  If the object write ever
        ; fails the table patch is rolled back, so callers never observe a
        ; half-patched state.
        old := AhkMagic.PatchBif(fnName, newName)
        try
            NumPut("Ptr", newAddr, rawPtr, off)
        catch as e {
            AhkMagic.RestoreBif(fnName, old)
            throw e
        }
        return Map(
            "fnName", fnName,
            "newName", newName,
            "oldPtr", old,
            "offset", off
        )
    }

    static RestoreBifObject(fnObj, state) {
        AhkMagic.Init()
        if !(fnObj is Func)
            throw TypeError("expected a Func object")
        fnName := fnObj.Name
        if state["fnName"] != fnName
            throw Error("state does not match function object " fnName)
        if !AhkMagic.bif.Has(state["newName"])
            throw Error("builtin not found: " state["newName"])
        rawPtr := ObjPtr(fnObj)
        current := AhkMagic.moduleBase + AhkMagic.bif[state["newName"]]["rva"]
        off := AhkMagic._FindBifPtrOffset(rawPtr, current)
        if off < 0
            throw Error("could not locate mBIF in function object for " fnName)
        NumPut("Ptr", state["oldPtr"], rawPtr, off)
        AhkMagic.RestoreBif(fnName, state["oldPtr"])
    }

    static _FindBifPtrOffset(rawPtr, targetAddr) {
        loop 64 {
            off := (A_Index - 1) * 8
            if NumGet(rawPtr, off, "Ptr") = targetAddr
                return off
        }
        return -1
    }

    ; General PE export-table scanner (works for any loaded DLL/EXE module).
    static ScanExports(moduleBase) {
        if !(moduleBase is Integer) or !moduleBase
            throw TypeError("moduleBase must be a nonzero module address", -1)
        if !AhkMagic.exportScanner
            AhkMagic.exportScanner := MCode(MC_PE_EXPORT_SCANNER_X64)
        out := Buffer(100 * 1024)
        rc := DllCall(
            AhkMagic.exportScanner.Ptr,
            "Ptr", moduleBase,
            "Ptr", out.Ptr,
            "Int"
        )
        if rc != 0
            throw Error("AhkScanExports failed with rc=" rc)
        count := NumGet(out, 8, "Int64")
        ; The scanner writes at most 4096 entries into the 100 KiB buffer.
        if count < 0 or count > 4096
            throw Error("export count out of scanner contract: " count, -1)
        result := Map()
        loop count {
            p := out.Ptr + 24 + (A_Index - 1) * 24
            namePtr := NumGet(p, 0, "Ptr")
            fnRva := NumGet(p, 8, "Int64")
            ordinal := NumGet(p, 16, "UInt")
            if !AhkMagic._Readable(namePtr, 8)
                throw Error("export entry " A_Index " has an invalid name pointer", -1)
            result[StrGet(namePtr, 1024, "UTF-8")] := Map(
                "rva", fnRva,
                "ordinal", ordinal
            )
        }
        return result
    }

    ; ------------------------------------------------------------------
    ; True in-process Eval.  Locates the interpreter's own expression
    ; compiler/evaluator, builds a temporary Line/ArgStruct, and calls
    ; them directly in this process.
    ; ------------------------------------------------------------------

    static _ModuleSections() {
        base := AhkMagic.moduleBase
        peOff := NumGet(base, 0x3C, "UInt")
        numSections := NumGet(base + peOff + 6, "UShort")
        optSize := NumGet(base + peOff + 20, "UShort")
        secTable := base + peOff + 24 + optSize
        result := Map()
        loop numSections {
            hdr := secTable + (A_Index - 1) * 40
            name := StrGet(hdr, 8, "UTF-8")
            vsize := NumGet(hdr + 8, "UInt")
            va := NumGet(hdr + 12, "UInt")
            result[name] := Map(
                "rva", va,
                "size", vsize,
                "ptr", base + va
            )
        }
        pdata := result.Has(".pdata") ? result[".pdata"] : 0
        for _, sec in result
            sec["pdata"] := pdata
        return result
    }

    static _TextSection(secs) {
        if secs.Has(".text")
            return secs[".text"]
        for name, sec in secs
            if name != ".rsrc"
                return sec
        throw Error("text section not found")
    }

    static _FindUtf16(secs, text) {
        len := StrLen(text)
        needle := Buffer(2 * (len + 1), 0)
        StrPut(text, needle, "UTF-16")
        hits := []
        for name, sec in secs {
            if name = ".rsrc"
                continue
            if !sec["ptr"] or sec["size"] < 2 * len
                continue
            p := sec["ptr"]
            count := sec["size"] >= 2 * len
                ? Min((sec["size"] - 2 * len) // 2 + 1, 0x100000)
                : 0
            if count <= 0
                continue
            loop count {
                off := (A_Index - 1) * 2
                ok := true
                loop len {
                    if NumGet(p + off + 2 * (A_Index - 1), "UShort")
                        != NumGet(needle, 2 * (A_Index - 1), "UShort") {
                        ok := false
                        break
                    }
                }
                if ok and NumGet(p + off + 2 * len, "UShort") = 0
                    hits.Push(sec["rva"] + off)
            }
        }
        return hits
    }

    static _RipRefs(sec, targetRva) {
        p := sec["ptr"]
        size := sec["size"]
        refs := []
        if size < 8
            return refs
        loop size - 7 {
            i := A_Index - 1
            b := NumGet(p + i, "UChar")
            if b != 0x48 and b != 0x4C
                continue
            if NumGet(p + i + 1, "UChar") != 0x8D
                continue
            reg := NumGet(p + i + 2, "UChar")
            if reg != 0x05 and reg != 0x0D and reg != 0x15 and reg != 0x1D
                and reg != 0x25 and reg != 0x2D and reg != 0x35 and reg != 0x3D
                continue
            disp := NumGet(p + i + 3, "Int")
            next := sec["rva"] + i + 7
            if next + disp = targetRva
                refs.Push(sec["rva"] + i)
        }
        return refs
    }

    static _PdataFunctionStart(pdata, rva, sec) {
        if !pdata or pdata["size"] < 12
            throw Error("PE .pdata section is missing", -1)
        p := pdata["ptr"]
        count := pdata["size"] // 12
        lo := 1
        hi := count
        idx := 0
        while lo <= hi {
            mid := (lo + hi) // 2
            off := (mid - 1) * 12
            begin := NumGet(p + off, "UInt")
            end := NumGet(p + off + 4, "UInt")
            if rva < begin {
                hi := mid - 1
                continue
            }
            if rva >= end {
                lo := mid + 1
                continue
            }
            idx := mid
            break
        }
        if !idx
            throw Error("function start not found for RVA "
            . Format("0x{:X}", rva), -1)
        loop idx {
            current := idx - A_Index + 1
            off := (current - 1) * 12
            begin := NumGet(p + off, "UInt")
            if AhkMagic._IsFunctionEntryCode(sec, begin)
                return begin
        }
        throw Error("logical function entry not found for RVA "
            . Format("0x{:X}", rva), -1)
    }

    static _PdataEntryStart(pdata, rva) {
        if !pdata or pdata["size"] < 12
            return 0
        p := pdata["ptr"]
        count := pdata["size"] // 12
        lo := 1
        hi := count
        while lo <= hi {
            mid := (lo + hi) // 2
            off := (mid - 1) * 12
            begin := NumGet(p + off, "UInt")
            end := NumGet(p + off + 4, "UInt")
            if rva < begin {
                hi := mid - 1
                continue
            }
            if rva >= end {
                lo := mid + 1
                continue
            }
            return begin
        }
        return 0
    }

    static _IsFunctionEntryCode(sec, rva) {
        off := rva - sec["rva"]
        if off < 0 or off + 4 > sec["size"]
            return false
        p := sec["ptr"]
        b0 := NumGet(p + off, "UChar")
        b1 := NumGet(p + off + 1, "UChar")
        b2 := NumGet(p + off + 2, "UChar")
        b3 := NumGet(p + off + 3, "UChar")
        if b0 = 0x48 and b1 = 0x89
            and ((b2 = 0x5C and b3 = 0x24)
                or (b2 = 0x54 and b3 = 0x24)
                or (b2 = 0x4C and b3 = 0x24))
            return true
        if b0 = 0x4C and b1 = 0x89
            and ((b2 = 0x44 and b3 = 0x24)
                or (b2 = 0x4C and b3 = 0x24))
            return true
        if b0 = 0x40 and b1 >= 0x50 and b1 <= 0x57
            return true
        if b0 = 0x55 or b0 = 0x53 or b0 = 0x56 or b0 = 0x57
            or b0 = 0x41
            return true
        return false
    }

    static _FnStart(sec, refRva) {
        if !sec.Has("pdata")
            throw Error("section has no PE .pdata context", -1)
        return AhkMagic._PdataFunctionStart(sec["pdata"], refRva, sec)
    }

    static _FindCallers(sec, targetRva) {
        p := sec["ptr"]
        size := sec["size"]
        calls := []
        if size < 6
            return calls
        loop size - 5 {
            i := A_Index - 1
            if NumGet(p + i, "UChar") != 0xE8
                continue
            disp := NumGet(p + i + 1, "Int")
            if sec["rva"] + i + 5 + disp = targetRva
                calls.Push(sec["rva"] + i)
        }
        return calls
    }

    static _BestStart(sec, refs) {
        starts := Map()
        for ref in refs {
            start := AhkMagic._FnStart(sec, ref)
            starts[start] := starts.Has(start) ? starts[start] + 1 : 1
        }
        best := 0
        bestCount := 0
        for start, count in starts {
            if count > bestCount {
                best := start
                bestCount := count
            }
        }
        return best
    }

    static _CurrLineSlotAddr() {
        AhkMagic.Init()
        if !AhkMagic.biv.Has("LineNumber")
            throw Error("A_LineNumber getter not found")
        p := AhkMagic.moduleBase + AhkMagic.biv["LineNumber"]["getter_rva"]
        if NumGet(p, "UChar") != 0x48 or NumGet(p + 1, "UChar") != 0x8B
            or NumGet(p + 2, "UChar") != 0x05
            throw Error("unexpected A_LineNumber getter code")
        disp := NumGet(p + 3, "Int")
        return p + 7 + disp
    }

    static _LocateInternalFunctions() {
        if AhkMagic.internalLocated
            return
        secs := AhkMagic._ModuleSections()
        text := AhkMagic._TextSection(secs)

        postfixRefs := AhkMagic._RipRefs(text, AhkMagic._FindUtf16(secs, "Missing operand.")[1])
        postfix2 := AhkMagic._BestStart(text, postfixRefs)
        if !postfix2
            throw Error("ExpressionToPostfix not found")

        expandRefs := AhkMagic._RipRefs(text, AhkMagic._FindUtf16(secs, "Error evaluating expression.")[1])
        expand := AhkMagic._BestStart(text, expandRefs)
        if !expand
            throw Error("ExpandExpression not found")

        AhkMagic.exprToPostfix := AhkMagic.moduleBase + postfix2
        AhkMagic.expandSingleArg := AhkMagic.moduleBase + expand
        AhkMagic.currLineSlot := AhkMagic._CurrLineSlotAddr()
        if !AhkMagic.internalLocator
            AhkMagic.internalLocator := MCode(MC_INTERNAL_LOCATOR_X64)
        locOut := Buffer(64, 0)
        rc := DllCall(
            AhkMagic.internalLocator.Ptr,
            "Ptr", AhkMagic.moduleBase,
            "UInt64", text["rva"],
            "UInt64", text["size"],
            "UInt64", postfix2,
            "UInt64", expand,
            "Ptr", locOut.Ptr,
            "Int"
        )
        if rc != 0
            throw Error("internal locator failed with rc=" rc)
        AhkMagic.gScript := NumGet(locOut, 0, "Ptr")
        AhkMagic.finalizeExpr := NumGet(locOut, 8, "Ptr")
        AhkMagic.findOrAddVar := NumGet(locOut, 16, "Ptr")
        AhkMagic.crtFree := NumGet(locOut, 24, "Ptr")
        AhkMagic.symInvalid := NumGet(locOut, 32, "UInt")
        AhkMagic.internalLocated := true
    }

    static _LocateCrtFree() {
        secs := AhkMagic._ModuleSections()
        text := AhkMagic._TextSection(secs)
        postfixRva := AhkMagic.exprToPostfix - AhkMagic.moduleBase
        callers := AhkMagic._FindCallers(text, postfixRva)
        for callerRva in callers {
            off := callerRva - text["rva"] + 5
            p := text["ptr"] + off
            loop 96 {
                i := A_Index - 1
                if NumGet(p + i, "UChar") != 0xE8
                    continue
                disp := NumGet(p + i + 1, "Int")
                target := text["rva"] + off + i + 5 + disp
                if target != postfixRva
                    return AhkMagic.moduleBase + target
            }
        }
        throw Error("CRT free not found")
    }

    static _LocatePreprocessFunc(sec) {
        p := sec["ptr"]
        size := sec["size"]
        count := size - 8
        loop count {
            i := A_Index - 1
            if NumGet(p + i, "UChar") != 0x41
                or NumGet(p + i + 1, "UChar") != 0x0F
                or NumGet(p + i + 2, "UChar") != 0xB6
                continue
            m := NumGet(p + i + 3, "UChar")
            if m != 0x76 and m != 0x7E
                continue
            if NumGet(p + i + 4, "UChar") != 0x23
                continue
            limit := Min(size - 4, i + 0x300)
            j := i + 5
            while j < limit {
                if NumGet(p + j, "UChar") = 0x80
                    and NumGet(p + j + 1, "UChar") = 0x78
                    and NumGet(p + j + 2, "UChar") = 0x23
                    and NumGet(p + j + 3, "UChar") = 0x02
                    return AhkMagic._FnStart(sec, sec["rva"] + i)
                j += 1
            }
        }
        return 0
    }

    static _LocateGptr(sec, preparseRva) {
        callers := AhkMagic._FindCallers(sec, preparseRva)
        for caller in callers {
            start := AhkMagic._FnStart(sec, caller)
            p := sec["ptr"]
            off := start - sec["rva"]
            limit := Min(sec["size"] - 16, off + 0x4000)
            i := off
            while i < limit {
                if NumGet(p + i, "UChar") = 0xCC
                    and NumGet(p + i + 1, "UChar") = 0xCC
                    break
                if NumGet(p + i, "UChar") = 0x48
                    and NumGet(p + i + 1, "UChar") = 0x8B
                    and NumGet(p + i + 2, "UChar") = 0x05 {
                    j := i + 7
                    while j < Min(i + 24, limit) {
                        b0 := NumGet(p + j, "UChar")
                        b1 := NumGet(p + j + 1, "UChar")
                        b2 := NumGet(p + j + 2, "UChar")
                        b3 := NumGet(p + j + 3, "UChar")
                        if b0 = 0x48
                            and b1 = 0x89
                            and (b2 = 0x58 or b2 = 0x50)
                            and (b3 = 0x28 or b3 = 0x50) {
                            disp := NumGet(p + i + 3, "Int")
                            return sec["rva"] + i + 7 + disp
                        }
                        j += 1
                    }
                }
                i += 1
            }
        }
        return 0
    }

    static _LocateLoadTs(sec, openRva) {
        callers := AhkMagic._FindCallers(sec, openRva)
        for caller in callers {
            p := sec["ptr"]
            base := sec["rva"]
            off := caller - base + 5
            limit := Min(sec["size"] - 5, off + 0x300)
            i := off
            while i < limit {
                isCmp := NumGet(p + i, "UChar") = 0x83
                    and NumGet(p + i + 1, "UChar") = 0xF8
                    and NumGet(p + i + 2, "UChar") = 0x03
                isCmp2 := NumGet(p + i, "UChar") = 0x3D
                    and NumGet(p + i + 1, "UChar") = 0x03
                if isCmp or isCmp2 {
                    j := i + 3
                    callLimit := Min(sec["size"] - 5, j + 0x200)
                    while j < callLimit {
                        if NumGet(p + j, "UChar") = 0xE8 {
                            disp := NumGet(p + j + 1, "Int")
                            target := base + j + 5 + disp
                            if AhkMagic._PdataEntryStart(sec["pdata"], target) = target
                                return target
                        }
                        j += 1
                    }
                    break
                }
                i += 1
            }
        }
        return 0
    }

    static _LocateOpenInclude(secs) {
        text := AhkMagic._TextSection(secs)
        anchors := [
            '%s file "%s" cannot be opened',
            '#Include'
        ]
        for anchor in anchors {
            hit := AhkMagic._FindUtf16(secs, anchor)
            if !hit.Length
                continue
            refs := AhkMagic._RipRefs(text, hit[1])
            start := AhkMagic._BestStart(text, refs)
            if start
                return start
        }
        return 0
    }

    static _LocateSrcCount(sec, openRva) {
        callers := AhkMagic._FindCallers(sec, openRva)
        for caller in callers {
            p := sec["ptr"]
            base := sec["rva"]
            off := caller - base
            i := off - 5
            min := Max(0, off - 0x100)
            while i >= min {
                b := NumGet(p + i, "UChar")
                if b = 0x8B
                    and (NumGet(p + i + 1, "UChar") = 0x2D
                    or NumGet(p + i + 1, "UChar") = 0x1D
                    or NumGet(p + i + 1, "UChar") = 0x35
                    or NumGet(p + i + 1, "UChar") = 0x3D) {
                    disp := NumGet(p + i + 2, "Int")
                    target := base + i + 6 + disp
                    if target > 0x10000 {
                        val := NumGet(AhkMagic.moduleBase + target, "Int")
                        if val >= 0 and val < 100000
                            return target
                    }
                }
                if b = 0x44
                    and NumGet(p + i + 1, "UChar") = 0x8B
                    and (NumGet(p + i + 2, "UChar") = 0x2D
                    or NumGet(p + i + 2, "UChar") = 0x3D) {
                    disp := NumGet(p + i + 3, "Int")
                    target := base + i + 7 + disp
                    if target > 0x10000 {
                        val := NumGet(AhkMagic.moduleBase + target, "Int")
                        if val >= 0 and val < 100000
                            return target
                    }
                }
                i -= 1
            }
        }
        return 0
    }

    static _LocateEvalScriptFunctions() {
        if AhkMagic.evalScriptLocated
            return
        AhkMagic._LocateInternalFunctions()
        secs := AhkMagic._ModuleSections()
        text := AhkMagic._TextSection(secs)
        postfixRva := AhkMagic.exprToPostfix - AhkMagic.moduleBase
        callers := AhkMagic._FindCallers(text, postfixRva)
        if !callers.Length
            throw Error("PreparseExpressions not found")
        preparse := AhkMagic._FnStart(text, callers[1])

        preprocess := AhkMagic._LocatePreprocessFunc(text)
        if !preprocess
            throw Error("PreprocessLocalVars not found")

        open := AhkMagic._LocateOpenInclude(secs)
        if !open
            throw Error("OpenIncludedFile not found")

        loadTs := AhkMagic._LocateLoadTs(text, open)
        if !loadTs
            throw Error("LoadIncludedFile(TextStream) not found")
        srcCount := 0
        if !RegExMatch(A_AhkVersion, "^2\.1")
            srcCount := AhkMagic._LocateSrcCount(text, open)

        gptr := AhkMagic._LocateGptr(text, preparse)
        if !gptr
            throw Error("g pointer not found")

        AhkMagic.evalPreparse := AhkMagic.moduleBase + preparse
        AhkMagic.evalPreprocess := AhkMagic.moduleBase + preprocess
        AhkMagic.evalOpenInclude := AhkMagic.moduleBase + open
        AhkMagic.evalLoadTs := AhkMagic.moduleBase + loadTs
        AhkMagic.evalSrcCount := srcCount ? AhkMagic.moduleBase + srcCount : 0
        AhkMagic.evalGptr := AhkMagic.moduleBase + gptr
        AhkMagic.evalScriptLocated := true
    }

    static _EvalStructOffsets() {
        if !AhkMagic.evalStructOffsets
            AhkMagic._DiscoverEvalLayout()
        return AhkMagic.evalStructOffsets
    }

    ; Assemble the 21-slot layout array consumed by the eval machine-code
    ; blob.  Every slot comes from runtime discovery; slots with no
    ; discovered equivalent are derived from a discovered anchor and
    ; validated by _ArgShapeInProc/_RemoteArgShape.
    static _BuildEvalLayout(structOffsets, shape) {
        layout := Buffer(21 * 8, 0)
        put := (slot, val) => NumPut("UInt64", val, layout, slot * 8)
        put(0, structOffsets["line_action"])
        put(1, structOffsets["line_argc"])
        put(2, 0)  ; line number: never written, purely cosmetic in errors
        put(3, structOffsets["line_arg"])
        put(4, shape["type"])
        put(5, structOffsets["arg_expression"])
        put(6, shape["len"])
        put(7, shape["text"])
        put(8, shape["deref"])
        put(9, structOffsets["arg_postfix"])
        put(10, shape["max_stack"])
        put(11, shape["max_alloc"])
        put(12, structOffsets["token_value"])
        put(13, structOffsets["token_symbol"])
        put(14, structOffsets["token_stride"])
        put(15, structOffsets["token_usage"])
        put(16, structOffsets["deref_marker"])
        put(17, structOffsets["deref_var"])
        put(18, structOffsets["deref_type"])
        put(19, structOffsets["deref_len"])
        put(20, Max(24, structOffsets["deref_len"] + 4
            , structOffsets["deref_marker"] + 8
            , structOffsets["deref_var"] + 8))
        return layout
    }

    ; Fallback layout for remote evaluation when the target layout has not
    ; been discovered yet.  The blob validates the assumed token layout
    ; before its first write and reports status 9 on a mismatch, so these
    ; defaults can never corrupt a target with a different layout.
    static _DefaultEvalLayout() {
        layout := Buffer(21 * 8, 0)
        vals := [0, 1, 0, 8, 0, 1, 4, 8, 16, 24, 32, 36
            , 0, 16, 24, 8, 0, 8, 16, 20, 24]
        loop 21
            NumPut("UInt64", vals[A_Index], layout, (A_Index - 1) * 8)
        return layout
    }

    ; Derive the ArgStruct fields from the discovered arg_postfix anchor and
    ; validate them against the live arg.  text=postfix-16, len=postfix-12,
    ; deref=postfix-8, type=isExpr-1, max_stack/max_alloc=postfix+8/+12.
    ; Every derived field must be self-consistent on the live arg or
    ; discovery fails loudly.
    static _ArgShapeInProc(argPtr, postfixOff, isexprOff) {
        typeOff := isexprOff - 1
        if typeOff < 0 or NumGet(argPtr, typeOff, "UChar") != 0
            throw Error("arg type field validation failed", -1)
        textOff := postfixOff - 16
        lenOff := postfixOff - 20
        derefOff := postfixOff - 8
        if textOff < 0 or lenOff < 0 or derefOff < 0
            throw Error("arg field derivation out of range", -1)
        textPtr := NumGet(argPtr, textOff, "Ptr")
        if !AhkMagic._Readable(textPtr, 8)
            throw Error("arg text pointer unreadable", -1)
        alen := NumGet(argPtr, lenOff, "UInt")
        if alen = 0 or alen > 4096
            throw Error("arg length field implausible: " alen, -1)
        if StrLen(StrGet(textPtr, alen, "UTF-16")) != alen
            throw Error("arg text/length mismatch", -1)
        dref := NumGet(argPtr, derefOff, "Ptr")
        if dref and !AhkMagic._Readable(dref, 24)
            throw Error("arg deref pointer unreadable", -1)
        return Map(
            "type", typeOff,
            "len", lenOff,
            "text", textOff,
            "deref", derefOff,
            "max_stack", postfixOff + 8,
            "max_alloc", postfixOff + 12)
    }

    ; Remote mirror of _ArgShapeInProc: find a live expression arg among the
    ; discovered functions and validate the derived field offsets by content.
    static _RemoteArgShape(h, layout, s) {
        for q in layout["funcs"] {
            try
                jump := AhkMagic._RPtr(h, q + layout["mjump_line_off"])
            catch
                continue
            if !jump
                continue
            try
                argc := NumGet(AhkMagic._RemoteRead(h, jump + s["line_argc"], 1)
                    , 0, "UChar")
            catch
                continue
            if !argc
                continue
            try
                argPtr := AhkMagic._RPtr(h, jump + s["line_arg"])
            catch
                continue
            try
                postfix := AhkMagic._RPtr(h, argPtr + s["arg_postfix"])
            catch
                continue
            if !postfix
                continue
            try {
                typeOff := s["arg_expression"] - 1
                if typeOff < 0
                    continue
                if NumGet(AhkMagic._RemoteRead(h, argPtr + typeOff, 1)
                    , 0, "UChar") != 0
                    continue
                textOff := s["arg_postfix"] - 16
                lenOff := s["arg_postfix"] - 20
                derefOff := s["arg_postfix"] - 8
                if textOff < 0 or lenOff < 0 or derefOff < 0
                    continue
                textPtr := AhkMagic._RPtr(h, argPtr + textOff)
                alen := NumGet(AhkMagic._RemoteRead(h, argPtr + lenOff, 4)
                    , 0, "UInt")
                if alen = 0 or alen > 4096
                    continue
                if StrLen(AhkMagic._RemoteReadString(h, textPtr, alen))
                    != alen
                    continue
                dref := AhkMagic._RPtr(h, argPtr + derefOff)
                if dref
                    AhkMagic._RemoteRead(h, dref, 24)
                return Map(
                    "type", typeOff,
                    "len", lenOff,
                    "text", textOff,
                    "deref", derefOff,
                    "max_stack", s["arg_postfix"] + 8,
                    "max_alloc", s["arg_postfix"] + 12)
            } catch
                continue
        }
        throw Error("remote arg shape not found", -1)
    }

    static _DiscoverCurrOff() {
        if AhkMagic.evalCurrOff
            return AhkMagic.evalCurrOff
        secs := AhkMagic._ModuleSections()
        text := AhkMagic._TextSection(secs)
        preparseRva := AhkMagic.evalPreparse - AhkMagic.moduleBase
        callers := AhkMagic._FindCallers(text, preparseRva)
        for caller in callers {
            p := text["ptr"]
            base := text["rva"]
            off := AhkMagic._FnStart(text, caller) - base
            limit := Min(text["size"] - 16, off + 0x4000)
            i := off
            while i < limit {
                if NumGet(p + i, "UChar") = 0xCC
                    and NumGet(p + i + 1, "UChar") = 0xCC
                    break
                if NumGet(p + i, "UChar") = 0x48
                    and NumGet(p + i + 1, "UChar") = 0x8B
                    and NumGet(p + i + 2, "UChar") = 0x05 {
                    j := i + 7
                    while j < Min(i + 24, limit) {
                        if NumGet(p + j, "UChar") = 0x48
                            and (NumGet(p + j + 1, "UChar") = 0x89)
                            and (NumGet(p + j + 2, "UChar") = 0x58
                                or NumGet(p + j + 2, "UChar") = 0x50) {
                            AhkMagic.evalCurrOff := NumGet(p + j + 3, "UChar")
                            return AhkMagic.evalCurrOff
                        }
                        j += 1
                    }
                }
                i += 1
            }
        }
        throw Error("g->curr offset not found")
    }

    static _DiscoverEvalLayout() {
        if AhkMagic.evalLayoutLocated
            return
        AhkMagic._LocateEvalScriptFunctions()
        AhkMagic._DiscoverCurrOff()
        gScript := AhkMagic.gScript
        g := NumGet(AhkMagic.evalGptr, "Ptr")
        savedProbeCur := NumGet(g, AhkMagic.evalCurrOff, "Ptr")
        gSnap := Buffer(0x100)
        DllCall("RtlMoveMemory", "Ptr", gSnap.Ptr, "Ptr", g, "UPtr", 0x100)
        NumPut("Ptr", 0, g, AhkMagic.evalCurrOff)
        snap := Buffer(0x200)
        DllCall("RtlMoveMemory", "Ptr", snap.Ptr, "Ptr", gScript, "UPtr", 0x200)
        oldSrcCount := AhkMagic.evalSrcCount ? NumGet(AhkMagic.evalSrcCount, "Int") : 0

        ; The probe function name is randomized so it can never collide with
        ; a function the host script already defines.
        probeName := "ahkHackLayoutProbe" Format("{:x}", A_TickCount)
        probe := probeName "() {`n    return A_Args.Length + 1`n}`n"

        ; The probe really loads a function and mutates interpreter state.
        ; Every failure below must restore the snapshot, otherwise the host
        ; interpreter is left with a stray function, a cleared g->curr and a
        ; modified source count.
        try {
            AhkMagic._LoadScriptMemory(probe)

            countOff := 0
            loop 0x200 // 4 {
                off := (A_Index - 1) * 4
                before := NumGet(snap, off, "Int")
                after := NumGet(gScript, off, "Int")
                if after = before + 1 and before >= 0 and after > 0 {
                    countOff := off
                    break
                }
            }
            if !countOff
                throw Error("probe load did not register a function: "
                    . "TextStream layout validation failed (mFuncsCount "
                    . "offset not found after rc=1 load)", -1)
            oldCount := NumGet(snap, countOff, "Int")

            funcsOff := 0
            loop 0x200 // 8 {
                off := (A_Index - 1) * 8
                ; gScript[0] is the variable array on every known build, and
                ; funcsOff=0 is also the "not found" sentinel below, so it
                ; must never participate in the match.
                if off = 0
                    continue
                p := NumGet(gScript, off, "Ptr")
                if !(p > 0x10000 and p < 0x7fffffffffff)
                    continue
                if DllCall("IsBadReadPtr", "Ptr", p, "UPtr", 0x100)
                    continue
                newFunc := NumGet(p, oldCount * 8, "Ptr")
                ; The new entry plus its two predecessors must all look like
                ; 8-aligned objects: unrelated heap garbage adjacent to other
                ; arrays must not impersonate mFuncs.
                ok := newFunc > 0x10000 and newFunc < 0x7fffffffffff
                    and Mod(newFunc, 8) = 0
                if ok and oldCount >= 2 {
                    prev := NumGet(p, (oldCount - 1) * 8, "Ptr")
                    prev2 := NumGet(p, (oldCount - 2) * 8, "Ptr")
                    ok := prev > 0x10000 and prev < 0x7fffffffffff
                        and Mod(prev, 8) = 0
                        and prev2 > 0x10000 and prev2 < 0x7fffffffffff
                        and Mod(prev2, 8) = 0
                }
                if ok {
                    funcsOff := off
                    break
                }
            }
            if !funcsOff
                throw Error("mFuncs offset not found")

            lastOff := -1
            loop 0x200 // 8 {
                off := (A_Index - 1) * 8
                if off = funcsOff
                    continue
                before := NumGet(snap, off, "Ptr")
                after := NumGet(gScript, off, "Ptr")
                if before != after and after > 0x10000 {
                    lastOff := off
                    break
                }
            }
            if lastOff < 0 {
                detail := "count=" countOff " funcs=" funcsOff
                loop 0x200 // 8 {
                    off := (A_Index - 1) * 8
                    b := NumGet(snap, off, "Ptr")
                    a := NumGet(gScript, off, "Ptr")
                    if b != a
                        detail .= " @" Format("{:X}", off) " " b "->" a
                            . " bad=" DllCall("IsBadReadPtr", "Ptr", a, "UPtr", 0x40)
                }
                throw Error("mLastLine offset not found: " detail)
            }

            arrPtr := NumGet(gScript, funcsOff, "Ptr")
            newFunc := NumGet(arrPtr, oldCount * 8, "Ptr")
            oldLast := NumGet(snap, lastOff, "Ptr")
            newLast := NumGet(gScript, lastOff, "Ptr")
            jumpOff := 0
            jumpLine := 0
            loop 0x200 // 8 {
                off := (A_Index - 1) * 8
                p := NumGet(newFunc, off, "Ptr")
                if p <= 0x10000 or p >= 0x7fffffffffff
                    continue
                try {
                    lineData := Buffer(0x100)
                    DllCall("RtlMoveMemory", "Ptr", lineData.Ptr, "Ptr", p
                        , "UPtr", 0x100)
                } catch
                    continue
                found := false
                loop lineData.Size // 8 {
                    if NumGet(lineData, (A_Index - 1) * 8, "Ptr") = newFunc {
                        found := true
                        break
                    }
                }
                if found {
                    jumpOff := off
                    jumpLine := p
                    break
                }
            }
            if !jumpOff
                throw Error("mJumpLine offset not found")

            structOffsets := AhkMagic._DiscoverInProcStructs(
                gScript, arrPtr, oldCount, newFunc, jumpOff)

            ; Derive and validate the remaining ArgStruct fields from the
            ; probe's own expression arg, then assemble the layout array the
            ; eval blob consumes.
            probeArg := NumGet(jumpLine, structOffsets["line_arg"], "Ptr")
            if !probeArg
                throw Error("probe arg not found", -1)
            argShape := AhkMagic._ArgShapeInProc(probeArg
                , structOffsets["arg_postfix"]
                , structOffsets["arg_expression"])

            AhkMagic.evalStructOffsets := structOffsets
            AhkMagic.evalLayoutArr := AhkMagic._BuildEvalLayout(
                structOffsets, argShape)
            AhkMagic.evalMFuncsOff := funcsOff
            AhkMagic.evalMFuncsCountOff := countOff
            AhkMagic.evalMLastLineOff := lastOff
            AhkMagic.evalMJumpLineOff := jumpOff
        } finally {
            ; Always roll the interpreter back to the pre-probe snapshot,
            ; whether discovery succeeded or not.
            DllCall("RtlMoveMemory", "Ptr", gScript, "Ptr", snap.Ptr, "UPtr", 0x200)
            if AhkMagic.evalSrcCount
                NumPut("Int", oldSrcCount, AhkMagic.evalSrcCount)
            DllCall("RtlMoveMemory", "Ptr", g, "Ptr", gSnap.Ptr, "UPtr", 0x100)
            NumPut("Ptr", savedProbeCur, g, AhkMagic.evalCurrOff)
        }

        AhkMagic._DiscoverParserOffsets()
        AhkMagic.evalLayoutLocated := true
    }

    ; Discover the parser-state field offsets in THIS process.  Both known
    ; region candidates (sentinel-anchored and qCmp-anchored) are built and
    ; validated against the live Script object; the strictly-validating
    ; candidate wins.  No version family is assumed.
    static _DiscoverParserOffsets() {
        if AhkMagic.evalParserLocated
            return
        secs := AhkMagic._ModuleSections()
        text := AhkMagic._TextSection(secs)
        p := text["ptr"]
        base := text["rva"]
        off := AhkMagic.evalLoadTs - AhkMagic.moduleBase - base
        limit := Min(text["size"] - 8, off + 0x100)
        qCmp := -1
        dCmp := -1
        i := off
        while i < limit {
            b0 := NumGet(p + i, "UChar")
            if b0 = 0x48 and NumGet(p + i + 1, "UChar") = 0x83 {
                modrm := NumGet(p + i + 2, "UChar")
                if modrm = 0x79 and NumGet(p + i + 4, "UChar") = 0 {
                    if qCmp < 0
                        qCmp := NumGet(p + i + 3, "UChar")
                } else if modrm = 0xB9 and NumGet(p + i + 7, "UChar") = 0 {
                    if qCmp < 0
                        qCmp := NumGet(p + i + 3, "Int")
                }
            }
            if b0 = 0x83 and NumGet(p + i + 1, "UChar") = 0xB9
                and (i = off or NumGet(p + i - 1, "UChar") != 0x48)
                and NumGet(p + i + 6, "UChar") = 0 {
                if dCmp < 0
                    dCmp := NumGet(p + i + 2, "Int")
            }
            if qCmp >= 0 and dCmp >= 0
                break
            i += 1
        }
        if qCmp < 0 or dCmp < 0
            throw Error("parser state anchors not found")

        ; Validate both candidates against the live Script object and pick
        ; the strictly-validating one.
        gScript := AhkMagic.gScript
        buf := Buffer(0x800)
        DllCall("RtlMoveMemory", "Ptr", buf.Ptr, "Ptr", gScript, "UPtr", 0x800)
        candidates := []
        if dCmp > 0 and dCmp + 4 <= buf.Size
            and NumGet(buf, dCmp, "Int") >= 0
            and NumGet(buf, dCmp, "Int") <= 100000 {
            exprIndexOff := 0
            loop Min(0x40, dCmp) // 4 {
                coff := dCmp - (A_Index - 1) * 4
                if coff + 8 > buf.Size
                    continue
                if NumGet(buf, coff, "Int") = 0x7fffffff
                    and NumGet(buf, coff + 4, "Int") = 0 {
                    exprIndexOff := coff + 4
                    break
                }
            }
            if exprIndexOff and exprIndexOff + 8 <= buf.Size
                candidates.Push(Map(
                    "rich", true,
                    "fields", Map(
                        "mclass_count", dCmp,
                        "mline_parent", exprIndexOff - 0x28,
                        "mpending_related", exprIndexOff - 0x20,
                        "mlast_param_init", exprIndexOff - 0x18,
                        "mpending_hotkey", exprIndexOff - 0x10,
                        "mexpr_func", exprIndexOff - 8,
                        "mexpr_func_index", exprIndexOff,
                        "mnext_func_body", exprIndexOff + 4,
                        "mignore_block", exprIndexOff + 5,
                        "mbackcompat", exprIndexOff + 6,
                        "mcurrent_module", exprIndexOff - 0x48,
                        "mlast_module", exprIndexOff - 0x40)))
        }
        if qCmp > 0 and qCmp + 0x28 <= buf.Size
            candidates.Push(Map(
                "fields", Map(
                    "mopen", qCmp,
                    "mpending_parent", qCmp + 8,
                    "mpending_related", qCmp + 16,
                    "mlast_param_init", qCmp + 24,
                    "mnext_func_body", qCmp + 32,
                    "mclass_count", dCmp)))

        best := 0
        bestScore := -1
        for cand in candidates {
            fields := cand["fields"]
            valid := true
            for key, foff in fields {
                ; Value-level checks are deliberately absent: the parser
                ; fields are saved and restored verbatim, so any plausible
                ; offset round-trips harmlessly.  The only hard requirement
                ; is that every offset stays inside the Script object.
                if !(foff is Integer) or foff <= 0 or foff >= 0x800
                    or foff + 8 > buf.Size {
                    valid := false
                    break
                }
            }
            if valid {
                ; The sentinel-anchored candidate has the richer field set
                ; and only exists when its anchor was actually found.
                score := cand.Has("rich") ? 10 : 0
                if score > bestScore {
                    bestScore := score
                    best := cand["fields"]
                }
            }
        }
        if !best
            throw Error("parser state layout candidates all failed validation"
                , -1)
        AhkMagic.evalParser := best
        AhkMagic.evalParserLocated := true
    }

    static EvalNative(expr) {
        AhkMagic.Init()
        if !(expr is String)
            throw TypeError("expr must be a string", -1)
        if StrLen(expr) > 4096
            throw ValueError("expr too long", -1)
        if !AhkMagic.inprocEval
            AhkMagic.inprocEval := MCode(MC_INPROC_EVAL_X64)
        AhkMagic._LocateInternalFunctions()
        ; The eval blob takes every Line/ArgStruct/token/deref offset from a
        ; runtime-discovered layout array; discover it once and reuse.
        AhkMagic._DiscoverEvalLayout()
        layoutArr := AhkMagic.evalLayoutArr
        if !layoutArr
            throw Error("eval layout not discovered", -1)

        scratch := Buffer(8 * 1024 * 1024, 0)
        out := Buffer(512, 0)
        exprBuf := Buffer((StrLen(expr) + 1) * 2, 0)
        StrPut(expr, exprBuf, "UTF-16")
        rc := DllCall(
            AhkMagic.inprocEval.Ptr,
            "Ptr", AhkMagic.exprToPostfix,
            "Ptr", AhkMagic.expandSingleArg,
            "Ptr", AhkMagic.currLineSlot,
            "Ptr", scratch.Ptr,
            "Ptr", exprBuf.Ptr,
            "Ptr", out.Ptr,
            "Int", 1,
            "Ptr", 0,
            "Ptr", 0,
            "Ptr", AhkMagic.gScript,
            "Ptr", AhkMagic.finalizeExpr,
            "Ptr", AhkMagic.findOrAddVar,
            "Ptr", AhkMagic.crtFree,
            "Ptr", AhkMagic.symInvalid,
            "Ptr", layoutArr.Ptr,
            "Int"
        )
        if rc != 0
            throw Error("EvalNative harness failed with rc=" rc)
        status := NumGet(out, 0, "UInt")
        if status = 9
            throw Error("EvalNative token layout mismatch; "
                . "unsupported runtime layout", -1)
        if status = 10
            throw Error("EvalNative layout validation failed", -1)
        if status != 0
            throw Error("EvalNative failed with status=" status)
        type := NumGet(out, 4, "UInt")
        if type = 1
            return NumGet(out, 8, "Int64")
        if type = 2
            return NumGet(out, 8, "Double")
        if type = 0
            return StrGet(NumGet(out, 16, "Ptr"), "UTF-16")
        if type = 5 {
            ; SYM_OBJECT: wrap the interpreter's live object.  The eval
            ; harness never finalizes its result token, so the object stays
            ; alive; the reference returned here belongs to the caller.
            ptr := NumGet(out, 8, "Ptr")
            if !ptr
                throw Error("EvalNative returned a null object", -1)
            return ObjFromPtrAddRef(ptr)
        }
        throw Error("EvalNative returned unknown symbol " type)
    }

    static Summary() {
        AhkMagic.Init()
        return Format(
            "module=0x{:X}`nbif={} @0x{:X} (stride 0x{:X})`n"
            . "mdfunc={} @0x{:X} (stride 0x{:X})`n"
            . "biv={} @0x{:X} (stride 0x{:X})",
            AhkMagic.moduleBase,
            AhkMagic.bifCount, AhkMagic.bifTablePtr, AhkMagic.bifStride,
            AhkMagic.mdfuncCount, AhkMagic.mdfuncTablePtr, AhkMagic.mdfuncStride,
            AhkMagic.bivCount, AhkMagic.bivTablePtr, AhkMagic.bivStride
        )
    }

    static Eval(expr) {
        AhkMagic.Init()
        if !(expr is String)
            throw TypeError("expr must be a string", -1)
        if expr = ""
            throw ValueError("expr must not be empty", -1)
        if RegExMatch(expr, "[\r\n]")
            return AhkMagic.EvalScript(expr)
        try {
            return AhkMagic.EvalNative(expr)
        } catch as e {
            ; Expressions the in-process pipeline cannot evaluate yet fall
            ; back to the explicit subprocess implementation.
        }
        return AhkMagic.EvalSubprocess(expr)
    }

    static EvalScript(text) {
        AhkMagic.Init()
        if !(text is String)
            throw TypeError("text must be a string", -1)
        if Trim(text) = ""
            throw ValueError("text must not be empty", -1)
        ; Pre-check with the interpreter's own /validate switch so a syntax
        ; error becomes a clean library error carrying the real parse
        ; message.  The validator captures its output and kills its process
        ; tree on timeout, so it never leaves a dialog on screen; the loader
        ; rc check below remains as the fallback guard.  A compiled host has
        ; no separate interpreter binary, so it keeps the fallback only.
        if !A_IsCompiled
            AhkMagic._ValidateScript(text, A_AhkPath)

        AhkMagic._DiscoverEvalLayout()
        off := AhkMagic._EvalStructOffsets()
        currOff := AhkMagic.evalCurrOff
        mfuncsOff := AhkMagic.evalMFuncsOff
        mfuncsCountOff := AhkMagic.evalMFuncsCountOff
        mlastLineOff := AhkMagic.evalMLastLineOff
        mjumpLineOff := AhkMagic.evalMJumpLineOff

        last := ""
        for line in StrSplit(text, "`n", "`r") {
            t := Trim(line)
            if t = ""
                continue
            if RegExMatch(t, "^(if|else|for|while|loop|try|catch|finally|return|break|continue|class|static|global|local|throw)\b")
                continue
            if SubStr(t, -1) = "{"
                continue
            last := t
        }
        if last = ""
            throw ValueError("no expression result found in script text", -1)

        gScript := AhkMagic.gScript
        oldLast := NumGet(gScript, mlastLineOff, "Ptr")
        oldFuncCount := NumGet(gScript, mfuncsCountOff, "Int")
        g := NumGet(AhkMagic.evalGptr, "Ptr")
        savedCur := NumGet(g, currOff, "Ptr")
        parser := AhkMagic.evalParser
        savedState := []
        for key in ["mopen", "mpending_parent", "mline_parent", "mpending_related"
            , "mlast_param_init", "mpending_hotkey", "mexpr_func", "mcurrent_module"] {
            if parser.Has(key)
                savedState.Push([key, "Ptr", NumGet(gScript, parser[key], "Ptr")])
        }
        for key in ["mexpr_func_index", "mclass_count"] {
            if parser.Has(key)
                savedState.Push([key, "Int", NumGet(gScript, parser[key], "Int")])
        }
        for key in ["mnext_func_body", "mignore_block", "mbackcompat"] {
            if parser.Has(key)
                savedState.Push([key, "UChar", NumGet(gScript, parser[key], "UChar")])
        }
        try {
            NumPut("Ptr", 0, g, currOff)

            if !AhkMagic.memScriptLoader
                AhkMagic.memScriptLoader := MCode(MC_MEM_SCRIPT_X64)
            textBuf := Buffer((StrLen(text) + 1) * 2, 0)
            StrPut(text, textBuf, "UTF-16")
            scratch := Buffer(0x400, 0)
            rc := DllCall(AhkMagic.memScriptLoader.Ptr
                , "Ptr", AhkMagic.evalLoadTs
                , "Ptr", gScript
                , "Ptr", AhkMagic.evalSrcCount
                , "Ptr", textBuf.Ptr
                , "UInt", StrLen(text) * 2
                , "Ptr", scratch.Ptr
                , "Int")
            if rc != 0
                throw Error("LoadIncludedFile(memory) failed with rc=" rc)

            funcsItem := NumGet(gScript, mfuncsOff, "Ptr")
            funcCount := NumGet(gScript, mfuncsCountOff, "Int")
            ; The count drives raw pointer walks below; refuse values that
            ; cannot come from one script injection.
            if funcCount < oldFuncCount
                or funcCount > oldFuncCount + 4096
                throw Error("mFuncs count changed unexpectedly: "
                    . oldFuncCount " -> " funcCount, -1)
            if funcCount > oldFuncCount {
                firstNew := oldLast ? NumGet(oldLast, off["line_next"], "Ptr") : 0
                if firstNew {
                    rcTail := DllCall(AhkMagic.evalPreparse
                        , "Ptr", gScript
                        , "Ptr", firstNew
                        , "Int")
                    if rcTail != 1
                        throw Error("PreparseExpressions(tail) failed with rc=" rcTail)
                }
                loop funcCount - oldFuncCount {
                    idx := oldFuncCount + A_Index - 1
                    newFunc := NumGet(funcsItem + idx * 8, "Ptr")
                    jump := NumGet(newFunc, mjumpLineOff, "Ptr")
                    if !jump
                        continue

                    rc2 := DllCall(AhkMagic.evalPreparse
                        , "Ptr", gScript
                        , "Ptr", jump
                        , "Int")
                    if rc2 != 1
                        throw Error("PreparseExpressions failed with rc=" rc2)

                    NumPut("Ptr", newFunc, g, currOff)
                    line := jump
                    lineSteps := 0
                    while line {
                        if lineSteps > 100000
                            throw Error("line chain too long; layout likely wrong", -1)
                        lineSteps += 1
                        if NumGet(line, off["line_action"], "UChar") = 3
                            and NumGet(line, off["line_attribute"], "Ptr")
                            NumPut("Ptr", NumGet(line, off["line_attribute"], "Ptr")
                                , g, currOff)
                        argc := NumGet(line, off["line_argc"], "UChar")
                        if argc {
                            arg := NumGet(line, off["line_arg"], "Ptr")
                            if arg and NumGet(arg, off["arg_expression"]
                                , "UChar") {
                                postfix := NumGet(arg, off["arg_postfix"], "Ptr")
                                if postfix {
                                    tokenSteps := 0
                                    while NumGet(postfix, off["token_symbol"], "UInt") != AhkMagic.symInvalid {
                                        if tokenSteps > 100000
                                            throw Error("postfix token chain too long; layout likely wrong", -1)
                                        tokenSteps += 1
                                        if NumGet(postfix, off["token_symbol"], "UInt") = 4
                                            and NumGet(postfix, off["token_usage"], "UInt") < 3 {
                                            deref := NumGet(postfix, off["token_value"], "Ptr")
                                            if deref {
                                                derefType := NumGet(deref
                                                    , off["deref_type"], "UChar")
                                                marker := NumGet(deref, off["deref_marker"], "Ptr")
                                                len := NumGet(deref, off["deref_len"], "UInt")
                                                if derefType = 7 {
                                                    NumPut("Ptr", NumGet(deref
                                                        , off["deref_var"], "Ptr")
                                                        , postfix, off["token_value"])
                                                } else if derefType = 0 and marker and len > 0 and len <= 64 {
                                                    var := DllCall(AhkMagic.findOrAddVar
                                                        , "Ptr", gScript
                                                        , "Ptr", marker
                                                        , "UPtr", len
                                                        , "UInt", 0x103
                                                        , "Ptr")
                                                    if var
                                                        NumPut("Ptr", var, postfix, off["token_value"])
                                                }
                                            }
                                        }
                                        postfix += off["token_stride"]
                                    }
                                }
                            }
                        }
                        line := NumGet(line, off["line_next"], "Ptr")
                    }
                    NumPut("Ptr", savedCur, g, currOff)
                    rc4 := DllCall(AhkMagic.evalPreprocess
                        , "Ptr", gScript
                        , "Ptr", newFunc
                        , "Int")
                    if rc4 != 1
                        throw Error("PreprocessLocalVars failed with rc=" rc4)
                }
            }
            ; Detach the newly added lines from the running line list so the
            ; active execution cannot continue into them.  The new function
            ; remains reachable through mFuncs.  The tail was already
            ; preparsed through firstNew above, so detaching now is safe.
            if oldLast and off.Has("line_next") and mlastLineOff >= 0 {
                NumPut("Ptr", 0, oldLast, off["line_next"])
                NumPut("Ptr", oldLast, gScript, mlastLineOff)
            }
            NumPut("Ptr", 0, g, currOff)
            r := AhkMagic.EvalNative(last)
            NumPut("Ptr", savedCur, g, currOff)
            return r
        } finally {
            for item in savedState
                NumPut(item[2], item[3], gScript, parser[item[1]])
            NumPut("Ptr", savedCur, g, currOff)
        }
    }

    static _LoadScriptMemory(text) {
        if !AhkMagic.memScriptLoader
            AhkMagic.memScriptLoader := MCode(MC_MEM_SCRIPT_X64)
        gScript := AhkMagic.gScript
        textBuf := Buffer((StrLen(text) + 1) * 2, 0)
        StrPut(text, textBuf, "UTF-16")
        scratch := Buffer(0x400, 0)
        rc := DllCall(AhkMagic.memScriptLoader.Ptr
            , "Ptr", AhkMagic.evalLoadTs
            , "Ptr", gScript
            , "Ptr", AhkMagic.evalSrcCount
            , "Ptr", textBuf.Ptr
            , "UInt", StrLen(text) * 2
            , "Ptr", scratch.Ptr
            , "Int")
        if rc != 0
            throw Error("LoadIncludedFile(memory) failed with rc=" rc)
    }
    ; ------------------------------------------------------------------
    ; Remote attach: read the three interpreter tables from another
    ; AutoHotkey process without injecting anything.
    ; ------------------------------------------------------------------

    static _RemoteOpen(pid, write := false) {
        access := 0x410
        if write
            access := 0x410 | 0x28
        h := DllCall("OpenProcess", "UInt", access, "Int", 0, "UInt", pid, "Ptr")
        if !h
            throw Error("OpenProcess failed", -1)
        return h
    }

    static _RemoteModuleBase(h, pid) {
        hSnap := DllCall("CreateToolhelp32Snapshot", "UInt", 0x18, "UInt", pid, "Ptr")
        if hSnap = -1 or !hSnap
            throw Error("CreateToolhelp32Snapshot failed", -1)
        buf := Buffer(1080)
        NumPut("UInt", 1080, buf, 0)
        best := Map("base", 0, "path", "")
        try {
            if !DllCall("Module32FirstW", "Ptr", hSnap, "Ptr", buf.Ptr)
                throw Error("Module32FirstW failed", -1)
            loop {
                modBase := NumGet(buf, 24, "Ptr")
                modName := StrGet(buf.Ptr + 48, 256, "UTF-16")
                modPath := StrGet(buf.Ptr + 560, 260, "UTF-16")
                if !best["base"] or InStr(modName, "AutoHotkey", false) {
                    best := Map("base", modBase, "path", modPath)
                    if InStr(modName, "AutoHotkey", false)
                        break
                }
                if !DllCall("Module32NextW", "Ptr", hSnap, "Ptr", buf.Ptr)
                    break
            }
        } finally {
            DllCall("CloseHandle", "Ptr", hSnap)
        }
        if !best["base"]
            throw Error("module base not found", -1)
        return best
    }

    static _RemotePidByName(name) {
        hSnap := DllCall("CreateToolhelp32Snapshot", "UInt", 0x2, "UInt", 0, "Ptr")
        if hSnap = -1 or !hSnap
            throw Error("CreateToolhelp32Snapshot failed", -1)
        buf := Buffer(568)
        NumPut("UInt", 568, buf, 0)
        pid := 0
        try {
            if !DllCall("Process32FirstW", "Ptr", hSnap, "Ptr", buf.Ptr)
                throw Error("Process32FirstW failed", -1)
            loop {
                exe := StrGet(buf.Ptr + 44, 260, "UTF-16")
                if StrCompare(exe, name, false) = 0 {
                    pid := NumGet(buf, 8, "UInt")
                    break
                }
                if !DllCall("Process32NextW", "Ptr", hSnap, "Ptr", buf.Ptr)
                    break
            }
        } finally {
            DllCall("CloseHandle", "Ptr", hSnap)
        }
        if !pid
            throw Error("process not found: " name, -1)
        return pid
    }

    static _RemoteRead(h, addr, size) {
        buf := Buffer(size)
        read := 0
        if !DllCall("ReadProcessMemory", "Ptr", h, "Ptr", addr, "Ptr", buf.Ptr
            , "UPtr", size, "UPtr*", &read)
            throw Error("ReadProcessMemory failed at 0x" Format("{:X}", addr), -1)
        ; A partial success silently returns a half-garbage buffer and
        ; corrupts every downstream parse; fail loudly instead.
        if read != size
            throw Error("ReadProcessMemory short read at 0x"
                . Format("{:X}", addr) ": " read "/" size, -1)
        return buf
    }

    static _RemoteSections(h, base) {
        dos := AhkMagic._RemoteRead(h, base, 0x40)
        e_lfanew := NumGet(dos, 0x3C, "UInt")
        pe := AhkMagic._RemoteRead(h, base + e_lfanew, 0x18)
        if NumGet(pe, 0, "UInt") != 0x4550
            throw Error("PE signature not found", -1)
        num := NumGet(pe, 6, "UShort")
        optSize := NumGet(pe, 20, "UShort")
        secTable := base + e_lfanew + 24 + optSize
        hdrs := AhkMagic._RemoteRead(h, secTable, num * 40)
        secs := Map()
        hasText := false
        hasData := false
        hasPdata := false
        loop num {
            off := (A_Index - 1) * 40
            name := StrGet(hdrs.Ptr + off, 8, "UTF-8")
            vsize := NumGet(hdrs, off + 8, "UInt")
            va := NumGet(hdrs, off + 12, "UInt")
            rawSize := NumGet(hdrs, off + 16, "UInt")
            size := vsize ? vsize : rawSize
            if name = ".text"
                hasText := true
            if name = ".rdata" or name = ".data"
                hasData := true
            if name = ".pdata"
                hasPdata := true
            secs[name] := Map("name", name, "rva", va, "size", size
                , "start", base + va, "end", base + va + size
                , "data", Buffer(0), "ptr", 0)
        }
        strict := hasText and hasData
        for _, sec in secs {
            if strict and sec["name"] != ".text"
                and sec["name"] != ".rdata" and sec["name"] != ".data"
                continue
            if sec["name"] = ".rsrc"
                continue
            if sec["size"] > 0 and sec["size"] < 0x800000 {
                try
                    sec["data"] := AhkMagic._RemoteRead(h, sec["start"], sec["size"])
                catch
                    sec["data"] := Buffer(0)
                sec["ptr"] := sec["data"].Ptr
            }
        }
        pdata := 0
        if hasPdata {
            sec := secs[".pdata"]
            if sec["size"] > 0 and sec["size"] < 0x800000 {
                try {
                    sec["data"] := AhkMagic._RemoteRead(h, sec["start"]
                        , sec["size"])
                    sec["ptr"] := sec["data"].Ptr
                    pdata := sec
                } catch
                    pdata := 0
            }
        }
        for _, sec in secs
            sec["pdata"] := pdata
        return secs
    }

    static _RemoteReadUtf16(secs, addr, maxLen := 64) {
        for _, sec in secs {
            if addr >= sec["start"] and addr < sec["end"]
                and sec["data"].Size > 0 {
                off := addr - sec["start"]
                if off < sec["data"].Size
                    return StrGet(sec["data"].Ptr + off, maxLen, "UTF-16")
            }
        }
        return ""
    }

    static _RemoteStrValid(secs, addr) {
        text := AhkMagic._RemoteReadUtf16(secs, addr, 64)
        if text = "" or StrLen(text) > 64
            return false
        return RegExMatch(text, "^[\x20-\x7E]+$") ? true : false
    }

    static _RemotePtrInText(secs, ptr) {
        hasText := false
        for _, sec in secs
            if sec["name"] = ".text"
                hasText := true
        for _, sec in secs {
            if ptr < sec["start"] or ptr >= sec["end"]
                continue
            if sec["name"] = ".text"
                return true
            if !hasText and sec["name"] != ".rsrc"
                return true
        }
        return false
    }

    static _RemoteTable(h, secs, base, kind) {
        if kind = "bif" {
            stride := 0x20
            anchor := "Abs"
        } else if kind = "mdfunc" {
            stride := 0x28
            anchor := "BlockInput"
        } else {
            stride := 0x18
            anchor := "AhkPath"
        }
        bestStart := 0
        bestCount := 0
        for _, sec in secs {
            if sec["data"].Size < 32
                continue
            p := sec["data"].Ptr
            maxOff := sec["data"].Size - 16
            off := 0
            while off <= maxOff {
                namePtr := NumGet(p + off, "Ptr")
                fnPtr := NumGet(p + off + 8, "Ptr")
                if AhkMagic._RemoteStrValid(secs, namePtr)
                    and AhkMagic._RemotePtrInText(secs, fnPtr) {
                    name := AhkMagic._RemoteReadUtf16(secs, namePtr, 64)
                    if name = anchor {
                        count := 1
                        prevName := name
                        q := off + stride
                        while q + 16 <= sec["data"].Size {
                            qNamePtr := NumGet(p + q, "Ptr")
                            qFnPtr := NumGet(p + q + 8, "Ptr")
                            if !AhkMagic._RemoteStrValid(secs, qNamePtr)
                                or !AhkMagic._RemotePtrInText(secs, qFnPtr)
                                break
                            qName := AhkMagic._RemoteReadUtf16(secs, qNamePtr, 64)
                            if StrCompare(qName, prevName, false) < 0
                                break
                            count += 1
                            prevName := qName
                            q += stride
                        }
                        if count > bestCount {
                            bestCount := count
                            bestStart := sec["rva"] + off
                        }
                    }
                }
                off += 8
            }
        }
        empty := Map("found", false, "stride", stride, "count", 0
            , "entries", Map(), "names", [])
        if bestCount < 10
            return empty
        sec := 0
        for _, s in secs
            if bestStart >= s["rva"] and bestStart < s["rva"] + s["size"] {
                sec := s
                break
            }
        if !sec or sec["data"].Size = 0
            return empty
        entries := Map()
        names := []
        off := bestStart - sec["rva"]
        loop bestCount {
            p := sec["data"].Ptr + off
            namePtr := NumGet(p, "Ptr")
            name := AhkMagic._RemoteReadUtf16(secs, namePtr, 64)
            if kind = "bif" {
                entries[name] := Map(
                    "rva", NumGet(p + 8, "Ptr") - base,
                    "min", NumGet(p + 16, "UChar"),
                    "max", NumGet(p + 17, "UChar"),
                    "fid", NumGet(p + 18, "UChar"))
            } else if kind = "mdfunc" {
                entries[name] := Map(
                    "rva", NumGet(p + 8, "Ptr") - base,
                    "ret", NumGet(p + 16, "UChar"))
            } else {
                getter := NumGet(p + 8, "Ptr")
                setter := NumGet(p + 16, "Ptr")
                entries[name] := Map(
                    "getter_rva", getter - base,
                    "setter_rva", setter ? setter - base : 0)
            }
            names.Push(name)
            off += stride
        }
        return Map("found", true, "table_rva", bestStart, "stride", stride
            , "count", bestCount, "entries", entries, "names", names)
    }

    static _RemoteLocateInternal(secs, base) {
        text := AhkMagic._TextSection(secs)
        if !text["ptr"]
            throw Error("remote text section not loaded", -1)
        postfixRefs := AhkMagic._RipRefs(text
            , AhkMagic._FindUtf16(secs, "Missing operand.")[1])
        postfix2 := AhkMagic._BestStart(text, postfixRefs)
        expandRefs := AhkMagic._RipRefs(text
            , AhkMagic._FindUtf16(secs, "Error evaluating expression.")[1])
        expand := AhkMagic._BestStart(text, expandRefs)
        if !postfix2 or !expand
            throw Error("remote expression functions not found", -1)
        return Map("postfix_rva", postfix2, "expand_rva", expand)
    }

    static _RemoteCurrLineSlot(secs, base, getterRva) {
        text := AhkMagic._TextSection(secs)
        p := text["ptr"]
        off := getterRva - text["rva"]
        if NumGet(p + off, "UChar") != 0x48
            or NumGet(p + off + 1, "UChar") != 0x8B
            or NumGet(p + off + 2, "UChar") != 0x05
            throw Error("unexpected remote A_LineNumber getter code", -1)
        disp := NumGet(p + off + 3, "Int")
        return base + getterRva + 7 + disp
    }

    static _HexBuffer(hex) {
        size := StrLen(hex) // 2
        buf := Buffer(size)
        loop size {
            byte := Integer("0x" SubStr(hex, 2 * A_Index - 1, 2))
            NumPut("UChar", byte, buf, A_Index - 1)
        }
        return buf
    }

    static _RemoteWrite(h, addr, buf) {
        written := 0
        if !DllCall("WriteProcessMemory", "Ptr", h, "Ptr", addr
            , "Ptr", buf.Ptr, "UPtr", buf.Size, "UPtr*", &written)
            throw Error("WriteProcessMemory failed", -1)
        if written != buf.Size
            throw Error("WriteProcessMemory short write at 0x"
                . Format("{:X}", addr) ": " written "/" buf.Size, -1)
    }

    static _RemoteReadString(h, addr, maxLen := 4096) {
        out := Buffer(0)
        chunk := 256
        loop {
            if out.Size >= maxLen
                break
            try
                part := AhkMagic._RemoteRead(h, addr + out.Size, chunk)
            catch
                break
            if part.Size = 0
                break
            combined := Buffer(out.Size + part.Size)
            if out.Size
                DllCall("RtlMoveMemory", "Ptr", combined.Ptr, "Ptr", out.Ptr
                    , "UPtr", out.Size)
            DllCall("RtlMoveMemory", "Ptr", combined.Ptr + out.Size
                , "Ptr", part.Ptr, "UPtr", part.Size)
            out := combined
            found := -1
            loop out.Size // 2 {
                if NumGet(out, (A_Index - 1) * 2, "UShort") = 0 {
                    found := A_Index - 1
                    break
                }
            }
            if found >= 0
                return StrGet(out.Ptr, found, "UTF-16")
        }
        return StrGet(out.Ptr, "UTF-16")
    }

    static AttachRemote(pid) {
        if !(pid is Integer)
            throw TypeError("pid must be an integer", -1)
        h := AhkMagic._RemoteOpen(pid, false)
        try {
            mod := AhkMagic._RemoteModuleBase(h, pid)
            secs := AhkMagic._RemoteSections(h, mod["base"])
            bif := AhkMagic._RemoteTable(h, secs, mod["base"], "bif")
            mdfunc := AhkMagic._RemoteTable(h, secs, mod["base"], "mdfunc")
            biv := AhkMagic._RemoteTable(h, secs, mod["base"], "biv")
            internal := Map()
            try {
                loc := AhkMagic._RemoteLocateInternal(secs, mod["base"])
                textSec := AhkMagic._TextSection(secs)
                loc["text_rva"] := textSec["rva"]
                loc["text_size"] := textSec["size"]
                if biv["found"] and biv["entries"].Has("LineNumber") {
                    loc["curr_line_slot"] := AhkMagic._RemoteCurrLineSlot(
                        secs, mod["base"]
                        , biv["entries"]["LineNumber"]["getter_rva"])
                    internal := loc
                }
            } catch as e {
                internal := Map("error", e.What " | " e.Message)
            }
            return Map(
                "pid", pid,
                "module", mod["path"],
                "image_base", mod["base"],
                "builtins", bif,
                "native_functions", mdfunc,
                "builtin_vars", biv,
                "internal", internal
            )
        } finally {
            DllCall("CloseHandle", "Ptr", h)
        }
    }

    static AttachRemoteByName(name) {
        if !(name is String)
            throw TypeError("name must be a string", -1)
        pid := AhkMagic._RemotePidByName(name)
        return AhkMagic.AttachRemote(pid)
    }

    static RemoteRedirect(hook, name, newName) {
        if !(hook is Map) or !hook.Has("pid")
            throw TypeError("hook must be an AttachRemote result", -1)
        bif := hook["builtins"]
        if !bif["found"] or !bif["entries"].Has(name)
            or !bif["entries"].Has(newName)
            throw Error("builtin redirect names not found", -1)
        idx := 0
        for n in bif["names"] {
            if n = name
                break
            idx += 1
        }
        if idx >= bif["names"].Length
            throw Error("builtin index not found", -1)
        h := AhkMagic._RemoteOpen(hook["pid"], true)
        try {
            fnSlot := hook["image_base"] + bif["table_rva"]
                + idx * bif["stride"] + 8
            newPtr := hook["image_base"] + bif["entries"][newName]["rva"]
            data := Buffer(8)
            NumPut("Ptr", newPtr, data, 0)
            written := 0
            if !DllCall("WriteProcessMemory", "Ptr", h, "Ptr", fnSlot
                , "Ptr", data.Ptr, "UPtr", 8, "UPtr*", &written)
                throw Error("WriteProcessMemory failed", -1)
        } finally {
            DllCall("CloseHandle", "Ptr", h)
        }
        return Map("src", name, "dst", newName, "fn_slot", fnSlot)
    }

    static RemoteDeepRedirect(hook, name, newName) {
        if !(hook is Map) or !hook.Has("pid")
            throw TypeError("hook must be an AttachRemote result", -1)
        bif := hook["builtins"]
        if !bif["found"] or !bif["entries"].Has(name)
            or !bif["entries"].Has(newName)
            throw Error("builtin redirect names not found", -1)
        idx := 0
        for n in bif["names"] {
            if n = name
                break
            idx += 1
        }
        if idx >= bif["names"].Length
            throw Error("builtin index not found", -1)

        h := AhkMagic._RemoteOpen(hook["pid"], true)
        try {
            slot := hook["image_base"] + bif["table_rva"] + idx * bif["stride"]
            slotBuf := AhkMagic._RemoteRead(h, slot, 16)
            namePtr := NumGet(slotBuf, 0, "Ptr")
            oldFn := NumGet(slotBuf, 8, "Ptr")
            newFn := hook["image_base"] + bif["entries"][newName]["rva"]

            ; The vtable check below needs the module image bounds; read
            ; SizeOfImage from the target's PE header once.  It sits at
            ; optional-header offset 56 (i.e. 24 + 56 into the PE header).
            dos := AhkMagic._RemoteRead(h, hook["image_base"], 0x40)
            peOff := NumGet(dos, 0x3C, "UInt")
            pe := AhkMagic._RemoteRead(h, hook["image_base"] + peOff, 0x80)
            imageSize := NumGet(pe, 80, "UInt")
            imageEnd := hook["image_base"] + imageSize

            patches := []
            addr := 0
            mbi := Buffer(48)
            loop {
                if !DllCall("VirtualQueryEx", "Ptr", h, "Ptr", addr
                    , "Ptr", mbi.Ptr, "UPtr", 48)
                    break
                state := NumGet(mbi, 32, "UInt")
                protect := NumGet(mbi, 36, "UInt")
                memType := NumGet(mbi, 40, "UInt")
                regionSize := NumGet(mbi, 24, "Int64")
                baseAddr := NumGet(mbi, 0, "Ptr")
                if state = 0x1000 and memType = 0x20000
                    and (protect & 0x4 or protect & 0x40)
                    and regionSize > 0 and regionSize < 0x8000000 {
                    chunkSize := 0x10000
                    off := 0
                    while off < regionSize {
                        want := Min(chunkSize, regionSize - off)
                        try
                            data := AhkMagic._RemoteRead(h, baseAddr + off, want)
                        catch {
                            off += want
                            continue
                        }
                        maxOff := data.Size - 8
                        pos := 0
                        while pos <= maxOff {
                            if NumGet(data, pos, "Ptr") = oldFn {
                                ok := false
                                start := Max(0, pos - 0x200)
                                end := Min(data.Size - 8, pos + 0x200)
                                j := start
                                while j <= end {
                                    if NumGet(data, j, "Ptr") = namePtr {
                                        ok := true
                                        break
                                    }
                                    j += 8
                                }
                                ; The name window alone can false-positive on
                                ; unrelated structs that happen to hold both
                                ; pointers.  A real Func object matches one
                                ; of two version layouts: 2.0 puts the
                                ; vtable pointer right before the function
                                ; pointer; 2.1 packs mMinParams/mMaxParams
                                ; into the qword before it.
                                if ok {
                                    vtOk := false
                                    for bc in [pos - 0x10, pos - 0x08] {
                                        if bc < 0 or bc + 8 > data.Size
                                            continue
                                        vt := NumGet(data, bc, "Ptr")
                                        if vt >= hook["image_base"]
                                            and vt < imageEnd {
                                            vtOk := true
                                            break
                                        }
                                    }
                                    if !vtOk and pos >= 0x10 {
                                        minP := bif["entries"][name]["min"]
                                        maxP := bif["entries"][name]["max"]
                                        packed := (minP << 32) | maxP
                                        if NumGet(data, pos - 0x10, "UInt64")
                                            = packed
                                            vtOk := true
                                    }
                                    if vtOk
                                        patches.Push(baseAddr + off + pos)
                                }
                            }
                            pos += 8
                        }
                        off += want
                    }
                }
                if regionSize <= 0
                    break
                nextAddr := baseAddr + regionSize
                if nextAddr <= addr
                    break
                addr := nextAddr
            }
            if !patches.Length
                throw Error("no Func object found for " name, -1)
            data := Buffer(8)
            NumPut("Ptr", newFn, data, 0)
            ; Snapshot every slot before writing so a mid-loop failure can
            ; roll the already-patched entries back instead of leaving the
            ; target half-redirected.
            originals := []
            for p in patches
                originals.Push(AhkMagic._RemoteRead(h, p, 8))
            written := []
            try {
                loop patches.Length {
                    AhkMagic._RemoteWrite(h, patches[A_Index], data)
                    written.Push(A_Index)
                }
            } catch as e {
                for idx in written {
                    try
                        AhkMagic._RemoteWrite(h, patches[idx], originals[idx])
                    catch
                        continue
                }
                throw e
            }
            return Map("src", name, "dst", newName, "count", patches.Length)
        } finally {
            DllCall("CloseHandle", "Ptr", h)
        }
    }

    static RemoteEval(hook, expr) {
        if !(hook is Map) or !hook.Has("pid") or !hook.Has("internal")
            or !hook["internal"].Has("postfix_rva")
            throw Error("hook has no remote eval context", -1)
        if !hook["internal"].Has("curr_line_slot")
            or !hook["internal"]["curr_line_slot"]
            throw Error("hook has no current-line slot; attach again", -1)
        if !(expr is String)
            throw TypeError("expr must be a string", -1)
        if StrLen(expr) > 4096
            throw ValueError("expr too long", -1)

        internal := hook["internal"]
        stub := AhkMagic._HexBuffer(MC_REMOTE_EVAL_STUB_X64)
        locator := AhkMagic._HexBuffer(MC_INTERNAL_LOCATOR_X64)
        evalBlob := AhkMagic._HexBuffer(MC_INPROC_EVAL_X64)
        codeSize := stub.Size + locator.Size + evalBlob.Size
        paramOff := (codeSize + 15) // 16 * 16
        layoutOff := paramOff + 136
        locOff := ((layoutOff + 21 * 8 + 15) // 16) * 16
        outOff := locOff + 64
        exprLen := (StrLen(expr) + 1) * 2
        exprOff := outOff + 512
        scratchOff := exprOff + exprLen
        total := scratchOff + 8 * 1024 * 1024

        ; Prefer the discovered target layout; fall back to the validated
        ; defaults when the hook has no layout yet (the blob aborts with a
        ; clear status before its first write if the assumption is wrong).
        layoutBuf := 0
        if hook.Has("script_layout") and hook["script_layout"].Has("struct")
            and hook["script_layout"].Has("shape") {
            layoutBuf := AhkMagic._BuildEvalLayout(
                hook["script_layout"]["struct"]
                , hook["script_layout"]["shape"])
        } else {
            layoutBuf := AhkMagic._DefaultEvalLayout()
        }

        h := AhkMagic._RemoteOpen(hook["pid"], true)
        try {
            block := DllCall("VirtualAllocEx", "Ptr", h, "Ptr", 0
                , "UPtr", total, "UInt", 0x3000, "UInt", 0x40, "Ptr")
            if !block
                throw Error("VirtualAllocEx failed", -1)
            threadExited := false
            try {
                AhkMagic._RemoteWrite(h, block, stub)
                AhkMagic._RemoteWrite(h, block + stub.Size, locator)
                AhkMagic._RemoteWrite(h, block + stub.Size + locator.Size, evalBlob)
                AhkMagic._RemoteWrite(h, block + layoutOff, layoutBuf)

                param := Buffer(136)
                NumPut("Ptr", block + stub.Size, param, 0)
                NumPut("Ptr", block + stub.Size + locator.Size, param, 8)
                NumPut("Ptr", hook["image_base"], param, 16)
                NumPut("UInt64", internal["text_rva"], param, 24)
                NumPut("UInt64", internal["text_size"], param, 32)
                NumPut("UInt64", internal["postfix_rva"], param, 40)
                NumPut("UInt64", internal["expand_rva"], param, 48)
                NumPut("Ptr", block + locOff, param, 56)
                NumPut("Ptr", hook["image_base"] + internal["postfix_rva"], param, 64)
                NumPut("Ptr", hook["image_base"] + internal["expand_rva"], param, 72)
                NumPut("Ptr", internal["curr_line_slot"], param, 80)
                NumPut("Ptr", block + scratchOff, param, 88)
                NumPut("Ptr", block + exprOff, param, 96)
                NumPut("Ptr", block + outOff, param, 104)
                NumPut("UInt64", 0, param, 112)
                NumPut("Int", 0, param, 120)
                NumPut("Int", 0, param, 124)
                NumPut("Ptr", block + layoutOff, param, 128)
                AhkMagic._RemoteWrite(h, block + paramOff, param)

                exprBuf := Buffer(exprLen)
                StrPut(expr, exprBuf, "UTF-16")
                AhkMagic._RemoteWrite(h, block + exprOff, exprBuf)

                tid := 0
                thread := DllCall("CreateRemoteThread", "Ptr", h, "Ptr", 0
                    , "UPtr", 0, "Ptr", block, "Ptr", block + paramOff
                    , "UInt", 0, "UInt*", &tid, "Ptr")
                if !thread
                    throw Error("CreateRemoteThread failed", -1)
                try {
                    wait := DllCall("WaitForSingleObject", "Ptr", thread
                        , "UInt", 10000)
                    if wait = 0x102 {
                        ; The remote thread is still executing the injected
                        ; block.  Freeing it would leave the target running
                        ; freed memory; leak the block and fail loudly.
                        throw Error("remote eval timed out after 10s; "
                            . "injected block leaked to protect the target", -1)
                    }
                    if wait = 0xFFFFFFFF
                        throw Error("WaitForSingleObject failed for remote eval", -1)
                    if wait != 0
                        throw Error("WaitForSingleObject returned 0x"
                            . Format("{:X}", wait) " for remote eval", -1)
                    threadExited := true
                    paramBack := AhkMagic._RemoteRead(h, block + paramOff, 136)
                    locRc := NumGet(paramBack, 120, "Int")
                    evalRc := NumGet(paramBack, 124, "Int")
                    if locRc != 0
                        throw Error("remote internal locator rc=" locRc, -1)
                    if evalRc != 0
                        throw Error("remote eval rc=" evalRc, -1)
                    out := AhkMagic._RemoteRead(h, block + outOff, 512)
                    status := NumGet(out, 0, "UInt")
                    type := NumGet(out, 4, "UInt")
                    if status != 0
                        throw Error("remote eval status=" status, -1)
                    if type = 1
                        return NumGet(out, 8, "Int64")
                    if type = 2
                        return NumGet(out, 8, "Double")
                    if type = 0 {
                        ptr := NumGet(out, 16, "Ptr")
                        return ptr ? AhkMagic._RemoteReadString(h, ptr) : ""
                    }
                    if type = 5 {
                        ; Objects cannot cross process boundaries; return the
                        ; object's address in the target so callers can use
                        ; it with the remote primitives.
                        ptr := NumGet(out, 8, "Ptr")
                        if !ptr
                            throw Error("remote eval returned a null object", -1)
                        return ptr
                    }
                    throw Error("remote eval returned unknown type " type, -1)
                } finally {
                    DllCall("CloseHandle", "Ptr", thread)
                }
            } finally {
                ; Only free the block once the remote thread has exited.
                ; On timeout the block is deliberately leaked (see above).
                if threadExited
                    DllCall("VirtualFreeEx", "Ptr", h, "Ptr", block, "UPtr", 0
                        , "UInt", 0x8000)
            }
        } finally {
            DllCall("CloseHandle", "Ptr", h)
        }
    }

    static _RemoteCall(h, fnAddr, args) {
        stub := AhkMagic._HexBuffer(MC_REMOTE_CALL_STUB_X64)
        param := Buffer(72)
        NumPut("Ptr", fnAddr, param, 0)
        loop 6 {
            val := args.Has(A_Index) ? args[A_Index] : 0
            NumPut("Ptr", val, param, 8 + 8 * (A_Index - 1))
        }
        NumPut("Int", 0, param, 56)
        block := DllCall("VirtualAllocEx", "Ptr", h, "Ptr", 0
            , "UPtr", stub.Size + 72, "UInt", 0x3000, "UInt", 0x40, "Ptr")
        if !block
            throw Error("VirtualAllocEx failed", -1)
        threadExited := false
        try {
            AhkMagic._RemoteWrite(h, block, stub)
            AhkMagic._RemoteWrite(h, block + stub.Size, param)
            tid := 0
            thread := DllCall("CreateRemoteThread", "Ptr", h, "Ptr", 0
                , "UPtr", 0, "Ptr", block, "Ptr", block + stub.Size
                , "UInt", 0, "UInt*", &tid, "Ptr")
            if !thread
                throw Error("CreateRemoteThread failed", -1)
            try {
                wait := DllCall("WaitForSingleObject", "Ptr", thread
                    , "UInt", 10000)
                if wait = 0x102 {
                    ; Thread still executing the injected block; freeing it
                    ; would corrupt the target, so leak it and fail loudly.
                    throw Error("remote call timed out after 10s; "
                        . "injected block leaked to protect the target", -1)
                }
                if wait = 0xFFFFFFFF
                    throw Error("WaitForSingleObject failed for remote call", -1)
                if wait != 0
                    throw Error("WaitForSingleObject returned 0x"
                        . Format("{:X}", wait) " for remote call", -1)
                threadExited := true
                back := AhkMagic._RemoteRead(h, block + stub.Size, 72)
                return NumGet(back, 56, "Int")
            } finally {
                DllCall("CloseHandle", "Ptr", thread)
            }
        } finally {
            if threadExited
                DllCall("VirtualFreeEx", "Ptr", h, "Ptr", block, "UPtr", 0
                    , "UInt", 0x8000)
        }
    }

    static _RemoteInternalLocator(h, secs, base, postfixRva, expandRva) {
        text := AhkMagic._TextSection(secs)
        locator := AhkMagic._HexBuffer(MC_INTERNAL_LOCATOR_X64)
        block := DllCall("VirtualAllocEx", "Ptr", h, "Ptr", 0
            , "UPtr", locator.Size + 64, "UInt", 0x3000, "UInt", 0x40, "Ptr")
        if !block
            throw Error("VirtualAllocEx failed", -1)
        try {
            AhkMagic._RemoteWrite(h, block, locator)
            locOut := block + locator.Size
            rc := AhkMagic._RemoteCall(h, block
                , [base, text["rva"], text["size"], postfixRva, expandRva, locOut])
            if rc != 0
                throw Error("remote internal locator rc=" rc, -1)
            return AhkMagic._RemoteRead(h, locOut, 64)
        } finally {
            DllCall("VirtualFreeEx", "Ptr", h, "Ptr", block, "UPtr", 0
                , "UInt", 0x8000)
        }
    }

    static _RemoteLoadScript(h, base, loc, text) {
        memScript := AhkMagic._HexBuffer(MC_MEM_SCRIPT_X64)
        textLen := (StrLen(text) + 1) * 2
        scratchSize := 0x400
        block := DllCall("VirtualAllocEx", "Ptr", h, "Ptr", 0
            , "UPtr", memScript.Size + textLen + scratchSize
            , "UInt", 0x3000, "UInt", 0x40, "Ptr")
        if !block
            throw Error("VirtualAllocEx failed", -1)
        try {
            AhkMagic._RemoteWrite(h, block, memScript)
            textAddr := block + memScript.Size
            scratchAddr := textAddr + textLen
            textBuf := Buffer(textLen)
            StrPut(text, textBuf, "UTF-16")
            AhkMagic._RemoteWrite(h, textAddr, textBuf)
            gscript := NumGet(loc["loc_out"], 0, "Ptr")
            loadTs := base + loc["load_ts_rva"]
            srcCount := loc.Has("src_count_rva") and loc["src_count_rva"]
                ? base + loc["src_count_rva"] : 0
            return AhkMagic._RemoteCall(h, block
                , [loadTs, gscript, srcCount, textAddr, StrLen(text) * 2
                    , scratchAddr])
        } finally {
            DllCall("VirtualFreeEx", "Ptr", h, "Ptr", block, "UPtr", 0
                , "UInt", 0x8000)
        }
    }

    static _RemoteCurrOff(secs, preparseRva) {
        text := AhkMagic._TextSection(secs)
        callers := AhkMagic._FindCallers(text, preparseRva)
        for caller in callers {
            p := text["ptr"]
            base := text["rva"]
            off := AhkMagic._FnStart(text, caller) - base
            limit := Min(text["size"] - 16, off + 0x4000)
            i := off
            while i < limit {
                if NumGet(p + i, "UChar") = 0xCC
                    and NumGet(p + i + 1, "UChar") = 0xCC
                    break
                if NumGet(p + i, "UChar") = 0x48
                    and NumGet(p + i + 1, "UChar") = 0x8B
                    and NumGet(p + i + 2, "UChar") = 0x05 {
                    j := i + 7
                    while j < Min(i + 24, limit) {
                        if NumGet(p + j, "UChar") = 0x48
                            and NumGet(p + j + 1, "UChar") = 0x89
                            and (NumGet(p + j + 2, "UChar") = 0x58
                            or NumGet(p + j + 2, "UChar") = 0x50)
                            return NumGet(p + j + 3, "UChar")
                        j += 1
                    }
                }
                i += 1
            }
        }
        return 0
    }

    static _RemoteParserAnchors(text, loadTsRva) {
        p := text["ptr"]
        base := text["rva"]
        off := loadTsRva - base
        limit := Min(text["size"] - 8, off + 0x100)
        qCmp := -1
        dCmp := -1
        i := off
        while i < limit {
            b0 := NumGet(p + i, "UChar")
            if b0 = 0x48 and NumGet(p + i + 1, "UChar") = 0x83 {
                modrm := NumGet(p + i + 2, "UChar")
                if modrm = 0x79 and NumGet(p + i + 4, "UChar") = 0
                    and qCmp < 0
                    qCmp := NumGet(p + i + 3, "UChar")
                else if modrm = 0xB9 and NumGet(p + i + 7, "UChar") = 0
                    and qCmp < 0
                    qCmp := NumGet(p + i + 3, "Int")
            }
            if b0 = 0x83 and NumGet(p + i + 1, "UChar") = 0xB9
                and (i = off or NumGet(p + i - 1, "UChar") != 0x48)
                and NumGet(p + i + 6, "UChar") = 0
                and dCmp < 0
                dCmp := NumGet(p + i + 2, "Int")
            if qCmp >= 0 and dCmp >= 0
                break
            i += 1
        }
        if qCmp < 0 or dCmp < 0
            throw Error("parser state anchors not found", -1)
        return Map("q", qCmp, "d", dCmp)
    }

    ; Build BOTH known parser-region candidate layouts and pick the one
    ; that validates against the live target state.  No version family is
    ; assumed: each candidate must satisfy its own structural checks
    ; (sentinel value, byte-field plausibility, pointer ranges) and the
    ; winner is the candidate that strictly validates.  Ambiguous or
    ; failing candidates raise an explicit error.
    static _RemoteDiscoverParser(h, gscript, qCmp, dCmp) {
        buf := AhkMagic._RemoteRead(h, gscript, 0x800)
        candidates := []
        scores := []

        ; Candidate A: sentinel-anchored region ending at mClassObjectCount.
        if dCmp > 0 and dCmp + 4 <= buf.Size
            and NumGet(buf, dCmp, "Int") >= 0
            and NumGet(buf, dCmp, "Int") <= 100000 {
            exprIndexOff := 0
            loop Min(0x40, dCmp) // 4 {
                off := dCmp - (A_Index - 1) * 4
                if off + 8 > buf.Size
                    continue
                if NumGet(buf, off, "Int") = 0x7fffffff
                    and NumGet(buf, off + 4, "Int") = 0 {
                    exprIndexOff := off + 4
                    break
                }
            }
            if exprIndexOff and exprIndexOff + 8 <= buf.Size
                candidates.Push(Map(
                    "rich", true,
                    "fields", Map(
                        "mclass_count", dCmp,
                        "mline_parent", exprIndexOff - 0x28,
                        "mpending_related", exprIndexOff - 0x20,
                        "mlast_param_init", exprIndexOff - 0x18,
                        "mpending_hotkey", exprIndexOff - 0x10,
                        "mexpr_func", exprIndexOff - 8,
                        "mexpr_func_index", exprIndexOff,
                        "mnext_func_body", exprIndexOff + 4,
                        "mignore_block", exprIndexOff + 5,
                        "mbackcompat", exprIndexOff + 6,
                        "mcurrent_module", exprIndexOff - 0x48,
                        "mlast_module", exprIndexOff - 0x40)))
        }

        ; Candidate B: qCmp-anchored region.
        if qCmp > 0 and qCmp + 0x28 <= buf.Size
            candidates.Push(Map(
                "fields", Map(
                    "mopen", qCmp,
                    "mpending_parent", qCmp + 8,
                    "mpending_related", qCmp + 16,
                    "mlast_param_init", qCmp + 24,
                    "mnext_func_body", qCmp + 32,
                    "mclass_count", dCmp)))

        best := 0
        bestScore := -1
        for cand in candidates {
            fields := cand["fields"]
            valid := true
            for key, off in fields {
                ; Value-level checks are deliberately absent: the parser
                ; fields are saved and restored verbatim, so any plausible
                ; offset round-trips harmlessly.  The only hard requirement
                ; is that every offset stays inside the Script object.
                if !(off is Integer) or off <= 0 or off >= 0x800
                    or off + 8 > buf.Size {
                    valid := false
                    break
                }
            }
            if valid {
                ; The sentinel-anchored candidate has the richer field set
                ; and only exists when its anchor was actually found.
                score := cand.Has("rich") ? 10 : 0
                if score > bestScore {
                    bestScore := score
                    best := cand
                }
            }
        }
        if !best
            throw Error("parser state layout candidates all failed validation"
                , -1)
        return best["fields"]
    }

    static _RemoteLocateEvalScript(secs, base) {
        text := AhkMagic._TextSection(secs)
        postfixRefs := AhkMagic._RipRefs(text
            , AhkMagic._FindUtf16(secs, "Missing operand.")[1])
        postfixRva := AhkMagic._BestStart(text, postfixRefs)
        expandRefs := AhkMagic._RipRefs(text
            , AhkMagic._FindUtf16(secs, "Error evaluating expression.")[1])
        expandRva := AhkMagic._BestStart(text, expandRefs)
        if !postfixRva or !expandRva
            throw Error("remote expression functions not found", -1)
        callers := AhkMagic._FindCallers(text, postfixRva)
        if !callers.Length
            throw Error("PreparseExpressions not found", -1)
        preparse := AhkMagic._FnStart(text, callers[1])
        preprocess := AhkMagic._LocatePreprocessFunc(text)
        if !preprocess
            throw Error("PreprocessLocalVars not found", -1)
        open := AhkMagic._LocateOpenInclude(secs)
        if !open
            throw Error("OpenIncludedFile not found", -1)
        loadTs := AhkMagic._LocateLoadTs(text, open)
        if !loadTs
            throw Error("LoadIncludedFile(TextStream) not found", -1)
        gptr := AhkMagic._LocateGptr(text, preparse)
        if !gptr
            throw Error("g pointer not found", -1)
        currOff := AhkMagic._RemoteCurrOff(secs, preparse)
        if !currOff
            throw Error("g->curr offset not found", -1)
        anchors := AhkMagic._RemoteParserAnchors(text, loadTs)
        return Map(
            "postfix_rva", postfixRva,
            "expand_rva", expandRva,
            "preparse_rva", preparse,
            "preprocess_rva", preprocess,
            "open_rva", open,
            "load_ts_rva", loadTs,
            "src_count_rva", 0,
            "gptr_rva", gptr,
            "curr_off", currOff,
            "q_cmp", anchors["q"],
            "d_cmp", anchors["d"],
            "parser", Map())
    }

    static _RemoteLineRows(h, funcs, funcData, jumpOff, backOff) {
        rows := []
        for index, q in funcs {
            linePtr := NumGet(funcData[index], jumpOff, "Ptr")
            if linePtr <= 0x10000 or linePtr >= 0x7fffffffffff
                continue
            try
                lineData := AhkMagic._RemoteRead(h, linePtr, 0x100)
            catch
                continue
            if backOff + 8 <= lineData.Size
                and NumGet(lineData, backOff, "Ptr") = q
                rows.Push(Map("func", q, "ptr", linePtr, "data", lineData))
        }
        return rows
    }

    static _RemoteLineHasFuncRef(lineData, q) {
        loop lineData.Size // 8 {
            if NumGet(lineData, (A_Index - 1) * 8, "Ptr") = q
                return true
        }
        return false
    }

    static _RemoteIsArgLike(h, p) {
        try
            argData := AhkMagic._RemoteRead(h, p, 40)
        catch
            return false
        if NumGet(argData, 0, "UChar") > 2
            or NumGet(argData, 1, "UChar") > 1
            return false
        if NumGet(argData, 4, "UInt") > 0x10000
            return false
        for off in [8, 16, 24] {
            v := NumGet(argData, off, "Ptr")
            if v and (v <= 0x10000 or v >= 0x7fffffffffff)
                return false
        }
        return true
    }

    static _RemoteTokenValid(data, stride, symOff, sentinel) {
        if symOff + 4 > stride
            return 0
        count := Min(data.Size // stride, 64)
        seenVar := false
        loop count {
            sym := NumGet(data, (A_Index - 1) * stride + symOff, "UInt")
            if sym = sentinel
                return seenVar ? A_Index : 0
            if sym > 0x1000
                return 0
            if sym = 4
                seenVar := true
        }
        return 0
    }

    static _RemoteCollectPostfixes(h, rows) {
        postfixes := []
        seen := Map()
        for row in rows {
            lineData := row["data"]
            loop lineData.Size // 8 {
                p := NumGet(lineData, (A_Index - 1) * 8, "Ptr")
                if p <= 0x10000 or p >= 0x7fffffffffff
                    continue
                if !AhkMagic._RemoteIsArgLike(h, p)
                    continue
                try
                    argData := AhkMagic._RemoteRead(h, p, 40)
                catch
                    continue
                loop argData.Size // 8 {
                    q := NumGet(argData, (A_Index - 1) * 8, "Ptr")
                    if q <= 0x10000 or q >= 0x7fffffffffff
                        continue
                    if seen.Has(q)
                        continue
                    seen[q] := true
                    postfixes.Push(q)
                }
            }
        }
        return postfixes
    }

    static _RemoteDiscoverTokens(h, postfixes, sentinel) {
        bestStride := 0
        bestSym := 0
        bestScore := -1
        for stride in [24, 32, 16, 40] {
            for symOff in [16, 20, 12, 8, 4, 0] {
                if symOff + 4 > stride
                    continue
                ok := 0
                bestLen := 0
                for p in postfixes {
                    try
                        data := AhkMagic._RemoteRead(h, p, stride * 64)
                    catch
                        continue
                    got := AhkMagic._RemoteTokenValid(data, stride, symOff
                        , sentinel)
                    if got {
                        ok += 1
                        bestLen := Max(bestLen, got)
                    }
                }
                score := ok * 10000 - bestLen - stride
                if score > bestScore {
                    bestScore := score
                    bestStride := stride
                    bestSym := symOff
                }
            }
        }
        if !bestStride
            throw Error("dynamic token layout not found", -1)

        varSym := 4
        usageOff := 8
        usageScore := -1
        for uOff in [8, 12, 4, 20] {
            if uOff = bestSym
                continue
            if uOff + 8 > bestStride
                continue
            score := 0
            for p in postfixes {
                try
                    data := AhkMagic._RemoteRead(h, p, bestStride * 64)
                catch
                    continue
                loop data.Size // bestStride {
                    off := (A_Index - 1) * bestStride
                    if NumGet(data, off + bestSym, "UInt") = varSym
                        and NumGet(data, off + uOff, "UInt64") < 0x1000
                        score += 1
                }
            }
            if score > usageScore {
                usageScore := score
                usageOff := uOff
            }
        }

        valueOff := 0
        valueScore := -1
        for vOff in [0, 8, 16] {
            if vOff = bestSym
                continue
            score := 0
            for p in postfixes {
                try
                    data := AhkMagic._RemoteRead(h, p, bestStride * 64)
                catch
                    continue
                loop data.Size // bestStride {
                    off := (A_Index - 1) * bestStride
                    if NumGet(data, off + bestSym, "UInt") = varSym {
                        v := NumGet(data, off + vOff, "Ptr")
                        if v > 0x10000 and v < 0x7fffffffffff
                            score += 1
                    }
                }
            }
            if score > valueScore {
                valueScore := score
                valueOff := vOff
            }
        }

        return Map(
            "stride", bestStride,
            "symbol", bestSym,
            "usage", usageOff,
            "value", valueOff,
            "var_symbol", varSym)
    }

    static _RemoteLineHeaderOff(rows) {
        bestAction := 0
        bestArgc := 1
        bestScore := -1
        loop 4 {
            ao := A_Index - 1
            loop 4 {
                ac := A_Index - 1
                if ac = ao
                    continue
                score := 0
                for row in rows {
                    data := row["data"]
                    if ao + 1 >= data.Size or ac + 1 >= data.Size
                        continue
                    if NumGet(data, ao, "UChar") <= 0x40
                        and NumGet(data, ac, "UChar") <= 0x10
                        score += 1
                }
                if score > bestScore {
                    bestScore := score
                    bestAction := ao
                    bestArgc := ac
                }
            }
        }
        if !bestScore
            throw Error("dynamic Line header layout not found", -1)
        return Map("action", bestAction, "argc", bestArgc)
    }

    static _RemoteLineArgOff(h, rows, tokens, sentinel) {
        counts := Map()
        argPostfixCounts := Map()
        for row in rows {
            lineData := row["data"]
            loop lineData.Size // 8 {
                lineOff := (A_Index - 1) * 8
                p := NumGet(lineData, lineOff, "Ptr")
                if p <= 0x10000 or p >= 0x7fffffffffff
                    continue
                if !AhkMagic._RemoteIsArgLike(h, p)
                    continue
                try
                    argData := AhkMagic._RemoteRead(h, p, 40)
                catch
                    continue
                loop argData.Size // 8 {
                    argOff := (A_Index - 1) * 8
                    q := NumGet(argData, argOff, "Ptr")
                    if q <= 0x10000 or q >= 0x7fffffffffff
                        continue
                    try
                        tokenData := AhkMagic._RemoteRead(h, q
                            , tokens["stride"] * 64)
                    catch
                        continue
                    if AhkMagic._RemoteTokenValid(tokenData
                        , tokens["stride"], tokens["symbol"], sentinel) {
                        counts[lineOff] := (counts.Has(lineOff)
                            ? counts[lineOff] : 0) + 1
                        argPostfixCounts[argOff] := (argPostfixCounts.Has(argOff)
                            ? argPostfixCounts[argOff] : 0) + 1
                    }
                }
            }
        }
        bestOff := 0
        bestScore := 0
        for off, score in counts {
            if score > bestScore or (score = bestScore and bestOff
                and off < bestOff) {
                bestScore := score
                bestOff := off
            }
        }
        bestArgPostfix := 24
        bestArgScore := 0
        for off, score in argPostfixCounts {
            if score > bestArgScore or (score = bestArgScore
                and bestArgPostfix and off < bestArgPostfix) {
                bestArgScore := score
                bestArgPostfix := off
            }
        }
        if !bestOff or !bestArgScore
            throw Error("dynamic Line.mArg / ArgStruct.postfix not found", -1)
        return Map("line_arg", bestOff, "arg_postfix", bestArgPostfix)
    }

    static _RemoteArgExpressionOff(h, rows, lineArg, argPostfix, tokens
        , sentinel) {
        counts := Map()
        for row in rows {
            arg := NumGet(row["data"], lineArg, "Ptr")
            if arg <= 0x10000 or arg >= 0x7fffffffffff
                continue
            try
                argData := AhkMagic._RemoteRead(h, arg, 40)
            catch
                continue
            postfix := NumGet(argData, argPostfix, "Ptr")
            if postfix <= 0x10000 or postfix >= 0x7fffffffffff
                continue
            try
                tokenData := AhkMagic._RemoteRead(h, postfix
                    , tokens["stride"] * 64)
            catch
                continue
            if !AhkMagic._RemoteTokenValid(tokenData, tokens["stride"]
                , tokens["symbol"], sentinel)
                continue
            loop 8 {
                off := A_Index - 1
                if NumGet(argData, off, "UChar") = 1
                    counts[off] := (counts.Has(off) ? counts[off] : 0) + 1
            }
        }
        bestOff := 1
        bestScore := 0
        for off, score in counts {
            if score > bestScore or (score = bestScore and off < bestOff) {
                bestScore := score
                bestOff := off
            }
        }
        if !bestScore
            throw Error("dynamic ArgStruct.is_expression not found", -1)
        return bestOff
    }

    static _RemoteLineNextOff(h, rows, lineArg, backOff) {
        forward := Map()
        total := Map()
        loop 0x100 // 8 {
            off := (A_Index - 1) * 8
            if off = lineArg
                continue
            for row in rows {
                p := NumGet(row["data"], off, "Ptr")
                if p <= 0x10000 or p >= 0x7fffffffffff
                    or p = row["ptr"]
                    continue
                try
                    lineData := AhkMagic._RemoteRead(h, p, 0x100)
                catch
                    continue
                if !AhkMagic._RemoteLineHasFuncRef(lineData, row["func"])
                    continue
                total[off] := (total.Has(off) ? total[off] : 0) + 1
                if p > row["ptr"]
                    forward[off] := (forward.Has(off) ? forward[off] : 0) + 1
            }
        }
        bestOff := 0
        bestForward := -1
        bestTotal := -1
        for off, score in forward {
            t := total.Has(off) ? total[off] : 0
            if score > bestForward
                or (score = bestForward and t > bestTotal)
                or (score = bestForward and t = bestTotal and bestOff
                    and off < bestOff) {
                bestForward := score
                bestTotal := t
                bestOff := off
            }
        }
        if !bestOff
            throw Error("dynamic Line.mNextLine not found", -1)
        return bestOff
    }

    static _RemoteLineAttributeOff(h, rows, lineArg, lineNext, backOff
        , funcs) {
        known := Map()
        for q in funcs
            known[q] := true
        counts := Map()
        for row in rows {
            visited := Map()
            stack := [row["ptr"]]
            while stack.Length {
                line := stack.Pop()
                if visited.Has(line)
                    continue
                visited[line] := true
                try
                    lineData := AhkMagic._RemoteRead(h, line, 0x100)
                catch
                    continue
                loop lineData.Size // 8 {
                    off := (A_Index - 1) * 8
                    if off = lineArg or off = lineNext
                        continue
                    p := NumGet(lineData, off, "Ptr")
                    if known.Has(p) {
                        counts[off] := (counts.Has(off)
                            ? counts[off] : 0) + 1
                        continue
                    }
                    if p <= 0x10000 or p >= 0x7fffffffffff or p = line
                        continue
                    if AhkMagic._RemoteLineHasFuncRef(lineData, row["func"])
                        and !visited.Has(p)
                        stack.Push(p)
                }
            }
        }
        bestOff := 0
        bestScore := 0
        for off, score in counts {
            if score > bestScore or (score = bestScore and bestOff
                and off < bestOff) {
                bestScore := score
                bestOff := off
            }
        }
        if !bestOff
            throw Error("dynamic Line.mAttribute not found", -1)
        return bestOff
    }

    static _RemoteDerefArrayLike(data) {
        if NumGet(data, 0, "Ptr") <= 0x10000
            or NumGet(data, 0, "Ptr") >= 0x7fffffffffff
            return false
        loop 24 {
            to := A_Index - 1
            if to < 8 or to + 8 > data.Size
                continue
            if NumGet(data, to, "UChar") <= 7
                and NumGet(data, to + 4, "UInt") <= 0x10000
                return true
        }
        return false
    }

    static _RemoteDerefLayout(h, rows, lineArg) {
        arrays := []
        argDerefCounts := Map()
        for row in rows {
            arg := NumGet(row["data"], lineArg, "Ptr")
            if arg <= 0x10000 or arg >= 0x7fffffffffff
                continue
            try
                argData := AhkMagic._RemoteRead(h, arg, 40)
            catch
                continue
            loop argData.Size // 8 {
                off := (A_Index - 1) * 8
                p := NumGet(argData, off, "Ptr")
                if p <= 0x10000 or p >= 0x7fffffffffff
                    continue
                try
                    data := AhkMagic._RemoteRead(h, p, 0x100)
                catch
                    continue
                if AhkMagic._RemoteDerefArrayLike(data) {
                    arrays.Push(data)
                    argDerefCounts[off] := (argDerefCounts.Has(off)
                        ? argDerefCounts[off] : 0) + 1
                }
            }
        }
        if arrays.Length < 1
            throw Error("dynamic ArgStruct.deref not found", -1)

        argDerefOff := 0
        argDerefScore := -1
        for off, score in argDerefCounts {
            if score > argDerefScore
                or (score = argDerefScore and off < argDerefOff) {
                argDerefScore := score
                argDerefOff := off
            }
        }
        if !argDerefScore
            throw Error("dynamic ArgStruct.deref offset not found", -1)

        bestStride := 24
        bestType := 16
        bestScore := -1
        for stride in [24, 32, 16, 40] {
            for typeOff in [16, 20, 24] {
                if typeOff + 8 > stride
                    continue
                score := 0
                for data in arrays {
                    loop Min(data.Size // stride, 8) {
                        off := (A_Index - 1) * stride
                        marker := NumGet(data, off, "Ptr")
                        if marker <= 0x10000
                            or marker >= 0x7fffffffffff
                            break
                        if NumGet(data, off + typeOff, "UChar") <= 7
                            and NumGet(data, off + typeOff + 1, "UChar") <= 3
                            and NumGet(data, off + typeOff + 4, "UInt")
                                <= 0x10000
                            score += 1
                    }
                }
                if score > bestScore
                    or (score = bestScore and typeOff < bestType)
                    or (score = bestScore and typeOff = bestType
                        and stride < bestStride) {
                    bestScore := score
                    bestType := typeOff
                    bestStride := stride
                }
            }
        }
        if !bestScore
            throw Error("dynamic DerefType layout not found", -1)

        markerOff := bestType - 16
        varOff := bestType - 8
        markerScore := 0
        for data in arrays {
            if NumGet(data, markerOff, "Ptr") > 0x10000
                and NumGet(data, markerOff, "Ptr") < 0x7fffffffffff
                markerScore += 1
        }
        if !markerScore
            throw Error("dynamic DerefType markers not found", -1)

        return Map(
            "arg_deref", argDerefOff,
            "stride", bestStride,
            "marker", markerOff,
            "var", varOff,
            "type", bestType,
            "len", bestType + 4)
    }

    static _RemoteDiscoverBackOff(h, funcs, funcData, jumpOff) {
        counts := Map()
        for index, q in funcs {
            linePtr := NumGet(funcData[index], jumpOff, "Ptr")
            if linePtr <= 0x10000 or linePtr >= 0x7fffffffffff
                continue
            try
                lineData := AhkMagic._RemoteRead(h, linePtr, 0x80)
            catch
                continue
            loop lineData.Size // 8 {
                bo := (A_Index - 1) * 8
                if NumGet(lineData, bo, "Ptr") = q
                    counts[bo] := (counts.Has(bo) ? counts[bo] : 0) + 1
            }
        }
        bestOff := 0
        bestCount := 0
        for off, count in counts {
            if count > bestCount or (count = bestCount and off < bestOff) {
                bestCount := count
                bestOff := off
            }
        }
        if !bestOff
            throw Error("back-reference slot not found", -1)
        return bestOff
    }

    static _DiscoverInProcStructs(gScript, arrPtr, oldCount, newFunc
        , jumpOff) {
        h := DllCall("OpenProcess", "UInt", 0x1F0FFF, "Int", 0
            , "UInt", DllCall("GetCurrentProcessId"), "Ptr")
        if !h
            throw Error("OpenProcess(self) failed", -1)
        try {
            funcs := []
            funcData := []
            loop oldCount {
                q := NumGet(arrPtr + (A_Index - 1) * 8, "Ptr")
                if q <= 0x10000 or q >= 0x7fffffffffff
                    continue
                funcs.Push(q)
                data := Buffer(0x400)
                DllCall("RtlMoveMemory", "Ptr", data.Ptr, "Ptr", q
                    , "UPtr", 0x400)
                funcData.Push(data)
            }
            funcs.Push(newFunc)
            data := Buffer(0x400)
            DllCall("RtlMoveMemory", "Ptr", data.Ptr, "Ptr", newFunc
                , "UPtr", 0x400)
            funcData.Push(data)
            backOff := AhkMagic._RemoteDiscoverBackOff(h, funcs, funcData
                , jumpOff)
            locOut := Buffer(64, 0)
            NumPut("Ptr", AhkMagic.gScript, locOut, 0)
            NumPut("Ptr", AhkMagic.finalizeExpr, locOut, 8)
            NumPut("Ptr", AhkMagic.findOrAddVar, locOut, 16)
            NumPut("Ptr", AhkMagic.crtFree, locOut, 24)
            NumPut("UInt", AhkMagic.symInvalid, locOut, 32)
            return AhkMagic._RemoteDiscoverStructs(h, locOut, funcs
                , funcData, jumpOff, backOff)
        } finally {
            DllCall("CloseHandle", "Ptr", h)
        }
    }

    static _RemoteDiscoverStructs(h, locOut, funcs, funcData, jumpOff
        , backOff) {
        rows := AhkMagic._RemoteLineRows(h, funcs, funcData, jumpOff, backOff)
        if rows.Length < 1
            throw Error("no ordinary functions for struct discovery"
                , -1)
        sentinel := NumGet(locOut, 32, "UInt")
        postfixes := AhkMagic._RemoteCollectPostfixes(h, rows)
        tokens := AhkMagic._RemoteDiscoverTokens(h, postfixes, sentinel)
        header := AhkMagic._RemoteLineHeaderOff(rows)
        arg := AhkMagic._RemoteLineArgOff(h, rows, tokens, sentinel)
        argExpr := AhkMagic._RemoteArgExpressionOff(h, rows
            , arg["line_arg"], arg["arg_postfix"], tokens, sentinel)
        lineNext := AhkMagic._RemoteLineNextOff(h, rows, arg["line_arg"]
            , backOff)
        lineAttr := AhkMagic._RemoteLineAttributeOff(h, rows
            , arg["line_arg"], lineNext, backOff, funcs)
        deref := AhkMagic._RemoteDerefLayout(h, rows, arg["line_arg"])
        return Map(
            "line_action", header["action"],
            "line_argc", header["argc"],
            "line_arg", arg["line_arg"],
            "line_attribute", lineAttr,
            "line_next", lineNext,
            "arg_expression", argExpr,
            "arg_postfix", arg["arg_postfix"],
            "arg_deref", deref["arg_deref"],
            "token_stride", tokens["stride"],
            "token_symbol", tokens["symbol"],
            "token_usage", tokens["usage"],
            "token_value", tokens["value"],
            "token_var_symbol", tokens["var_symbol"],
            "deref_stride", deref["stride"],
            "deref_marker", deref["marker"],
            "deref_var", deref["var"],
            "deref_type", deref["type"],
            "deref_len", deref["len"])
    }

    ; Discover the mFuncs layout of a target.  The validator needs live
    ; function entries, so a target whose script defines no functions
    ; (fresh/empty scripts) fails discovery.  Inject two probe functions
    ; through the target's own loader and retry once; the probes stay in
    ; the target's mFuncs as harmless extras.
    static _RemoteDiscoverLayoutWithProbe(h, secs, base, loc) {
        try {
            return AhkMagic._RemoteDiscoverLayout(h, secs, base, loc)
        } catch as e {
            if !InStr(e.Message, "dynamic function layout not found")
                and !InStr(e.Message, "dynamic token layout not found")
                throw e
            tag := Format("{:x}", A_TickCount)
            probe := "ahkHackLayoutProbeA" tag
                . "(x) {`n    return x + 1`n}`n"
                . "ahkHackLayoutProbeB" tag
                . "(y) {`n    return y * 2`n}`n"
            rc := AhkMagic._RemoteLoadScript(h, base, loc, probe)
            if rc != 0
                throw Error("probe injection for layout discovery failed rc="
                    . rc " (TextStream layout validation failed in target)"
                    , -1)
            try {
                ; First pass discovers mFuncs/mjumpLine without structs.
                layout := AhkMagic._RemoteDiscoverLayout(
                    h, secs, base, loc, false)
                ; The injected functions were loaded without preparse, so
                ; their lines have no postfix buffers yet.  Preparse them
                ; through the target's own pipeline, then finish struct
                ; discovery on the now-complete probe rows.
                savedCur := AhkMagic._RPtr(h, layout["g"] + layout["curr_off"])
                for q in layout["funcs"] {
                    jump := AhkMagic._RPtr(h
                        , q + layout["mjump_line_off"])
                    if !jump
                        continue
                    AhkMagic._WPtr(h, layout["g"] + layout["curr_off"], q)
                    prc := AhkMagic._RemoteCall(h
                        , base + loc["preparse_rva"]
                        , [layout["gscript"], jump])
                    AhkMagic._WPtr(h, layout["g"] + layout["curr_off"], savedCur)
                    if prc != 1
                        throw Error("probe PreparseExpressions rc=" prc, -1)
                }
                layout["struct"] := AhkMagic._RemoteDiscoverStructs(
                    h, loc["loc_out"], layout["funcs"], layout["func_data"]
                    , layout["mjump_line_off"], layout["jump_back_off"])
                layout["shape"] := AhkMagic._RemoteArgShape(h, layout
                    , layout["struct"])
                return layout
            } catch as e2 {
                throw Error("layout discovery failed even after probe "
                    . "injection: " e2.Message, -1)
            }
        }
    }

    static _RemoteDiscoverLayout(h, secs, base, loc, doStructs := true) {
        locOut := AhkMagic._RemoteInternalLocator(h, secs, base
            , loc["postfix_rva"], loc["expand_rva"])
        loc["loc_out"] := locOut
        gscript := NumGet(locOut, 0, "Ptr")
        gAddr := base + loc["gptr_rva"]
        g := AhkMagic._RPtr(h, gAddr)
        currOff := loc["curr_off"]
        snap := AhkMagic._RemoteRead(h, gscript, 0x800)
        bestScore := -1
        best := Map("funcs_off", 0, "count_off", 0, "arr_ptr", 0
            , "count", 0, "name_off", 0, "jump_off", 0
            , "back_off", 0, "funcs", [], "func_data", [])

        loop 0x200 // 8 {
            off := (A_Index - 1) * 8
            if off + 12 > snap.Size
                continue
            arrPtr := NumGet(snap, off, "Ptr")
            count := NumGet(snap, off + 8, "Int")
            if arrPtr <= 0x10000 or arrPtr >= 0x7fffffffffff
                or count < 1 or count > 100000
                continue

            limit := Min(count, 16)
            funcs := []
            funcData := []
            lineCache := Map()
            valid := true
            loop limit {
                try
                    q := AhkMagic._RPtr(h, arrPtr + (A_Index - 1) * 8)
                catch {
                    valid := false
                    break
                }
                if q <= 0x10000 or q >= 0x7fffffffffff {
                    valid := false
                    break
                }
                funcs.Push(q)
                funcData.Push(AhkMagic._RemoteRead(h, q, 0x400))
            }
            if !valid
                continue

            jumpOff := 0
            jumpMatches := 0
            jumpBestSlot := 0
            jumpSlotOff := 0
            loop 0x200 // 8 {
                jo := (A_Index - 1) * 8
                matches := 0
                slotCount := Map()
                for index, q in funcs {
                    linePtr := NumGet(funcData[index], jo, "Ptr")
                    if linePtr <= 0x10000 or linePtr >= 0x7fffffffffff
                        continue
                    if lineCache.Has(linePtr) {
                        lineData := lineCache[linePtr]
                    } else {
                        try {
                            lineData := AhkMagic._RemoteRead(h, linePtr, 0x80)
                            lineCache[linePtr] := lineData
                        } catch {
                            continue
                        }
                    }
                    foundSlot := 0
                    loop lineData.Size // 8 {
                        bo := (A_Index - 1) * 8
                        if NumGet(lineData, bo, "Ptr") = q {
                            foundSlot := bo
                            break
                        }
                    }
                    if foundSlot {
                        matches += 1
                        slotCount[foundSlot] := (slotCount.Has(foundSlot)
                            ? slotCount[foundSlot] : 0) + 1
                    }
                }
                bestSlot := 0
                bestSlotCount := 0
                for slot, count in slotCount {
                    if count > bestSlotCount or count = bestSlotCount {
                        bestSlotCount := count
                        bestSlot := slot
                    }
                }
                if bestSlotCount > jumpBestSlot
                    or (bestSlotCount = jumpBestSlot and jumpBestSlot
                        and jo < jumpOff) {
                    jumpMatches := matches
                    jumpBestSlot := bestSlotCount
                    jumpSlotOff := bestSlot
                    jumpOff := jo
                }
            }

            minJump := limit < 2 ? 1 : 2
            if jumpBestSlot < minJump
                continue
            ; A real mFuncs array must have back-reference rows for a
            ; majority of its entries.  Decoy arrays (other object lists in
            ; the Script object) can contain a few function-like pointers
            ; and win the name-based score; rejecting sub-majority
            ; back-reference counts keeps the scan on the real array.
            if jumpBestSlot * 2 < limit
                continue

            nameOff := 0
            nameMatches := 0
            nameUnique := 0
            loop 0x200 // 8 {
                no := (A_Index - 1) * 8
                matches := 0
                seen := Map()
                for index, q in funcs {
                    namePtr := NumGet(funcData[index], no, "Ptr")
                    if namePtr <= 0x10000 or namePtr >= 0x7fffffffffff
                        continue
                    try
                        name := AhkMagic._RemoteReadString(h, namePtr, 128)
                    catch
                        continue
                    if name = "" or StrLen(name) > 127
                        continue
                    if !RegExMatch(name, "^[A-Za-z_][A-Za-z0-9_.]*$")
                        continue
                    matches += 1
                    seen[name] := 1
                }
                if matches > nameMatches
                    or (matches = nameMatches and seen.Count > nameUnique) {
                    nameMatches := matches
                    nameUnique := seen.Count
                    nameOff := no
                }
            }

            minName := limit < 2 ? 1 : 2
            if nameMatches < minName
                continue

            score := nameMatches * 100000 + nameUnique * 1000
                + jumpMatches * 100 + limit
            if score > bestScore {
                bestScore := score
                best["funcs_off"] := off
                best["count_off"] := off + 8
                best["arr_ptr"] := arrPtr
                best["count"] := count
                best["name_off"] := nameOff
                best["jump_off"] := jumpOff
                best["back_off"] := jumpSlotOff
                best["funcs"] := funcs
                best["func_data"] := funcData
            }
        }

        if !best["funcs_off"] or !best["name_off"] or !best["jump_off"]
            throw Error("dynamic function layout not found", -1)

        loc["parser"] := AhkMagic._RemoteDiscoverParser(h, gscript
            , loc["q_cmp"], loc["d_cmp"])
        layout := Map(
            "gscript", gscript,
            "g", g,
            "curr_off", currOff,
            "mfuncs_off", best["funcs_off"],
            "mfuncs_count_off", best["count_off"],
            "mlast_line_off", 0,
            "mjump_line_off", best["jump_off"],
            "name_off", best["name_off"],
            "jump_back_off", best["back_off"],
            "funcs", best["funcs"],
            "func_data", best["func_data"],
            "parser", loc["parser"])
        if doStructs {
            layout["struct"] := AhkMagic._RemoteDiscoverStructs(h, locOut
                , best["funcs"], best["func_data"], best["jump_off"]
                , best["back_off"])
            layout["shape"] := AhkMagic._RemoteArgShape(h, layout
                , layout["struct"])
        }
        return layout
    }

    static _RPtr(h, addr) {
        return NumGet(AhkMagic._RemoteRead(h, addr, 8), 0, "Ptr")
    }

    static _RInt(h, addr) {
        return NumGet(AhkMagic._RemoteRead(h, addr, 4), 0, "Int")
    }

    static _WPtr(h, addr, val) {
        buf := Buffer(8)
        NumPut("Ptr", val, buf, 0)
        AhkMagic._RemoteWrite(h, addr, buf)
    }

    static _WInt(h, addr, val) {
        buf := Buffer(4)
        NumPut("Int", val, buf, 0)
        AhkMagic._RemoteWrite(h, addr, buf)
    }

    static _WByte(h, addr, val) {
        buf := Buffer(1)
        NumPut("UChar", val, buf, 0)
        AhkMagic._RemoteWrite(h, addr, buf)
    }

    static _DeclaredFunctionNames(text) {
        names := []
        depth := 0
        candidate := ""
        for raw in StrSplit(text, "`n", "`r") {
            line := Trim(raw)
            if candidate = "" and line != "" and SubStr(line, 1, 1) != ";" {
                if RegExMatch(line
                    , "^([A-Za-z_][A-Za-z0-9_]*)\s*\(", &m)
                    and !RegExMatch(line
                        , "^(if|else|for|while|switch|try|catch|finally"
                        . "|return|break|continue|global|local|static|class"
                        . "|new|throw|until)\b") {
                    candidate := m[1]
                }
            }
            if candidate != "" and InStr(line, "{") {
                found := false
                for n in names {
                    if n = candidate {
                        found := true
                        break
                    }
                }
                if !found and depth = 0
                    names.Push(candidate)
                candidate := ""
            }
            opens := 0
            closes := 0
            loop StrLen(line) {
                c := SubStr(line, A_Index, 1)
                if c = "{"
                    opens += 1
                else if c = "}"
                    closes += 1
            }
            depth += opens - closes
        }
        return names
    }

    static RemoteEvalScript(hook, text) {
        if !(hook is Map) or !hook.Has("pid")
            throw TypeError("hook must be an AttachRemote result", -1)
        if !(text is String) or Trim(text) = ""
            throw TypeError("text must be a non-empty string", -1)
        ; Same protection as the in-process path, but validated with the
        ; TARGET's own interpreter when its path is known.
        vpath := hook.Has("module") and hook["module"] != ""
            ? hook["module"] : A_AhkPath
        AhkMagic._ValidateScript(text, vpath)

        h := AhkMagic._RemoteOpen(hook["pid"], true)
        try {
            mod := AhkMagic._RemoteModuleBase(h, hook["pid"])
            secs := AhkMagic._RemoteSections(h, mod["base"])
            if hook.Has("script_loc") and hook.Has("script_layout") {
                loc := hook["script_loc"]
                layout := hook["script_layout"]
            } else {
                loc := AhkMagic._RemoteLocateEvalScript(secs, mod["base"])
                layout := AhkMagic._RemoteDiscoverLayoutWithProbe(
                    h, secs, mod["base"], loc)
                hook["script_loc"] := loc
                hook["script_layout"] := layout
            }
            locOut := loc["loc_out"]
            if !layout.Has("struct")
                layout["struct"] := AhkMagic._RemoteDiscoverStructs(h, locOut
                    , layout["funcs"], layout["func_data"]
                    , layout["mjump_line_off"], layout["jump_back_off"])
            if !layout.Has("shape")
                layout["shape"] := AhkMagic._RemoteArgShape(h, layout
                    , layout["struct"])
            s := layout["struct"]
            gscript := layout["gscript"]
            g := layout["g"]
            currOff := layout["curr_off"]
            for name in AhkMagic._DeclaredFunctionNames(text) {
                if AhkMagic._RemoteFindUserFuncs(h, layout, name).Length
                    throw Error("function name already exists in target: "
                        . name, -1)
            }
            oldFuncCount := AhkMagic._RInt(h, gscript + layout["mfuncs_count_off"])
            savedCur := AhkMagic._RPtr(h, g + currOff)
            savedCurFunc := AhkMagic._RPtr(h, g + currOff)

            parser := layout["parser"]
            savedState := []
            for key in ["mopen", "mpending_parent", "mline_parent"
                , "mpending_related", "mlast_param_init", "mpending_hotkey"
                , "mexpr_func", "mcurrent_module"]
                if parser.Has(key)
                    savedState.Push([key, "Ptr"
                        , AhkMagic._RPtr(h, gscript + parser[key])])
            for key in ["mexpr_func_index", "mclass_count"]
                if parser.Has(key)
                    savedState.Push([key, "Int"
                        , AhkMagic._RInt(h, gscript + parser[key])])
            for key in ["mnext_func_body", "mignore_block", "mbackcompat"]
                if parser.Has(key)
                    savedState.Push([key, "UChar"
                        , NumGet(AhkMagic._RemoteRead(h, gscript + parser[key], 1)
                            , 0, "UChar")])

            try {
                for key in ["mopen", "mpending_parent", "mline_parent"
                    , "mpending_related", "mlast_param_init"
                    , "mpending_hotkey", "mexpr_func"]
                    if parser.Has(key)
                        AhkMagic._WPtr(h, gscript + parser[key], 0)
                if parser.Has("mexpr_func_index")
                    AhkMagic._WInt(h, gscript + parser["mexpr_func_index"]
                        , 0x7fffffff)
                if parser.Has("mnext_func_body")
                    AhkMagic._WByte(h, gscript + parser["mnext_func_body"], 0)
                if parser.Has("mignore_block")
                    AhkMagic._WByte(h, gscript + parser["mignore_block"], 0)
                if parser.Has("mbackcompat")
                    AhkMagic._WByte(h, gscript + parser["mbackcompat"], 1)
                if parser.Has("mclass_count")
                    AhkMagic._WInt(h, gscript + parser["mclass_count"], 0)
                AhkMagic._WPtr(h, g + currOff, 0)

                rc := AhkMagic._RemoteLoadScript(h, mod["base"], loc, text)
                if rc != 0
                    throw Error("LoadIncludedFile(memory) rc=" rc, -1)

                funcsItem := AhkMagic._RPtr(h, gscript + layout["mfuncs_off"])
                funcCount := AhkMagic._RInt(h, gscript + layout["mfuncs_count_off"])
                if funcCount < oldFuncCount
                    or funcCount > oldFuncCount + 4096
                    throw Error("mFuncs count changed unexpectedly: "
                        . oldFuncCount " -> " funcCount, -1)
                if funcCount > oldFuncCount {
                    loop funcCount - oldFuncCount {
                        idx := oldFuncCount + A_Index - 1
                        newFunc := AhkMagic._RPtr(h, funcsItem + idx * 8)
                        jump := AhkMagic._RPtr(h
                            , newFunc + layout["mjump_line_off"])
                        if !jump
                            continue
                        AhkMagic._WPtr(h, g + currOff, newFunc)
                        rc := AhkMagic._RemoteCall(h
                            , mod["base"] + loc["preparse_rva"]
                            , [gscript, jump])
                        if rc != 1
                            throw Error("PreparseExpressions rc=" rc, -1)
                        AhkMagic._WPtr(h, g + currOff, newFunc)
                        line := jump
                        lineSteps := 0
                        while line {
                            if lineSteps > 100000
                                throw Error("remote line chain too long; layout likely wrong", -1)
                            lineSteps += 1
                            lineSize := Max(0x40, s["line_next"] + 8
                                , s["line_attribute"] + 8, s["line_arg"] + 8)
                            lineData := AhkMagic._RemoteRead(h, line, lineSize)
                            action := NumGet(lineData, s["line_action"], "UChar")
                            attr := NumGet(lineData, s["line_attribute"], "Ptr")
                            argc := NumGet(lineData, s["line_argc"], "UChar")
                            arg := NumGet(lineData, s["line_arg"], "Ptr")
                            if action = 3 and attr
                                AhkMagic._WPtr(h, g + currOff, attr)
                            if argc and arg
                                and NumGet(AhkMagic._RemoteRead(h
                                    , arg + s["arg_expression"], 1)
                                    , 0, "UChar") {
                                postfix := AhkMagic._RPtr(h
                                    , arg + s["arg_postfix"])
                                if postfix {
                                    tokenSteps := 0
                                    loop {
                                        if tokenSteps > 100000
                                            throw Error("remote postfix token chain too long; layout likely wrong", -1)
                                        tokenSteps += 1
                                        tokenSize := Max(s["token_stride"]
                                            , s["token_symbol"] + 4
                                            , s["token_usage"] + 8
                                            , s["token_value"] + 8)
                                        token := AhkMagic._RemoteRead(h
                                            , postfix, tokenSize)
                                        symbol := NumGet(token
                                            , s["token_symbol"], "UInt")
                                        if symbol = NumGet(locOut, 32, "UInt")
                                            break
                                        if symbol = s["token_var_symbol"]
                                            and NumGet(token, s["token_usage"]
                                                , "UInt") < 3 {
                                            deref := NumGet(token
                                                , s["token_value"], "Ptr")
                                            if deref {
                                                derefSize := Max(24
                                                    , s["deref_len"] + 4
                                                    , s["deref_type"] + 1
                                                    , s["deref_marker"] + 8
                                                    , s["deref_var"] + 8)
                                                derefData := AhkMagic._RemoteRead(h
                                                    , deref, derefSize)
                                                derefType := NumGet(derefData
                                                    , s["deref_type"], "UChar")
                                                marker := NumGet(derefData
                                                    , s["deref_marker"], "Ptr")
                                                derefLen := NumGet(derefData
                                                    , s["deref_len"], "UInt")
                                                if derefType = 7 {
                                                    AhkMagic._WPtr(h, postfix
                                                        , AhkMagic._RPtr(h
                                                            , deref
                                                            + s["deref_var"]))
                                                } else if derefType = 0
                                                    and marker
                                                    and derefLen > 0
                                                    and derefLen <= 64 {
                                                    var := AhkMagic._RemoteCall(h
                                                        , NumGet(locOut, 16, "Ptr")
                                                        , [gscript, marker
                                                            , derefLen, 0x103])
                                                    if var
                                                        AhkMagic._WPtr(h
                                                            , postfix
                                                                + s["token_value"]
                                                            , var)
                                                }
                                            }
                                        }
                                        postfix += s["token_stride"]
                                    }
                                }
                            }
                            line := AhkMagic._RPtr(h
                                , line + s["line_next"])
                        }
                        AhkMagic._WPtr(h, g + currOff, savedCur)
                        rc := AhkMagic._RemoteCall(h
                            , mod["base"] + loc["preprocess_rva"]
                            , [gscript, newFunc])
                        if rc != 1
                            throw Error("PreprocessLocalVars rc=" rc, -1)
                        AhkMagic._WPtr(h, g + currOff, savedCurFunc)
                    }
                }
                AhkMagic._WPtr(h, g + currOff, 0)

                last := ""
                for raw in StrSplit(text, "`n", "`r") {
                    t := Trim(raw)
                    if t = ""
                        continue
                    if RegExMatch(t
                        , "^(if|else|for|while|loop|try|catch|finally|return|break|continue|class|static|global|local|throw)\b")
                        continue
                    if SubStr(t, -1) = "{"
                        continue
                    last := t
                }
                if last = ""
                    throw ValueError("no expression result found in script text")
                return AhkMagic.RemoteEval(hook, last)
            } finally {
                for item in savedState {
                    if item[2] = "Ptr"
                        AhkMagic._WPtr(h, gscript + parser[item[1]], item[3])
                    else if item[2] = "Int"
                        AhkMagic._WInt(h, gscript + parser[item[1]], item[3])
                    else
                        AhkMagic._WByte(h, gscript + parser[item[1]], item[3])
                }
                AhkMagic._WPtr(h, g + currOff, savedCurFunc)
                AhkMagic._WPtr(h, g + currOff, savedCur)
            }
        } finally {
            DllCall("CloseHandle", "Ptr", h)
        }
    }

    static RemoteReplaceFuncBody(hook, oldName, newName) {
        if !(hook is Map) or !hook.Has("pid")
            throw TypeError("hook must be an AttachRemote result", -1)
        h := AhkMagic._RemoteOpen(hook["pid"], true)
        try {
            mod := AhkMagic._RemoteModuleBase(h, hook["pid"])
            secs := AhkMagic._RemoteSections(h, mod["base"])
            if hook.Has("script_layout") {
                layout := hook["script_layout"]
            } else {
                loc := AhkMagic._RemoteLocateEvalScript(secs, mod["base"])
                layout := AhkMagic._RemoteDiscoverLayoutWithProbe(
                    h, secs, mod["base"], loc)
                hook["script_loc"] := loc
                hook["script_layout"] := layout
            }
            oldList := AhkMagic._RemoteFindUserFuncs(h, layout, oldName)
            newPtr := AhkMagic._RemoteFindUserFunc(h, layout, newName)
            if !oldList.Length or !newPtr
                throw Error("function object not found: " oldName " / " newName, -1)
            jumpOff := layout["mjump_line_off"]
            newJump := AhkMagic._RPtr(h, newPtr + jumpOff)
            if !newJump
                throw Error("new function body not found", -1)
            patched := []
            for oldPtr in oldList {
                AhkMagic._WPtr(h, oldPtr + jumpOff, newJump)
                patched.Push(oldPtr)
            }
            return Map("old", oldList, "new", newPtr, "jump_off", jumpOff
                , "count", patched.Length)
        } finally {
            DllCall("CloseHandle", "Ptr", h)
        }
    }

    static _RemoteFindUserFunc(h, layout, name) {
        list := AhkMagic._RemoteFindUserFuncs(h, layout, name)
        return list.Length ? list[1] : 0
    }

    static _RemoteFindUserFuncs(h, layout, name) {
        gscript := layout["gscript"]
        arr := AhkMagic._RPtr(h, gscript + layout["mfuncs_off"])
        count := AhkMagic._RInt(h, gscript + layout["mfuncs_count_off"])
        if count < 0 or count > 100000
            throw Error("mFuncs count out of range: " count, -1)
        result := []
        shortName := name
        dot := InStr(name, ".")
        fullName := ""
        if dot {
            shortName := SubStr(name, dot + 1)
            fullName := SubStr(name, 1, dot - 1)
                . ".Prototype." shortName
        }
        loop count {
            nf := AhkMagic._RPtr(h, arr + (A_Index - 1) * 8)
            candidates := [name, shortName]
            if fullName != ""
                candidates.Push(fullName)
            for want in candidates {
                data := AhkMagic._RemoteRead(h, nf, 0x500)
                found := false
                loop data.Size // 2 - StrLen(want) {
                    off := (A_Index - 1) * 2
                    ok := true
                    loop StrLen(want) {
                        if NumGet(data, off + (A_Index - 1) * 2, "UShort")
                            != Ord(SubStr(want, A_Index, 1)) {
                            ok := false
                            break
                        }
                    }
                    if ok and NumGet(data, off + StrLen(want) * 2, "UShort") = 0 {
                        found := true
                        break
                    }
                }
                if found
                    result.Push(nf)
            }
        }
        return result
    }

    ; Validate text with the interpreter's own /validate switch before it
    ; reaches the loader, so a syntax error becomes a clean library error
    ; carrying the real parse message instead of a dialog or an aborted
    ; host process (in-process EvalScript) or target (RemoteEvalScript).
    ; The validator is driven by its captured output and a completion
    ; marker file, never by process exit codes, and its process tree is
    ; killed the moment the error text lands so no build can leave a
    ; dialog on screen.  When no signal arrives within six seconds the
    ; check is treated as inconclusive and the loader's own failure check
    ; remains the fallback guard.
    static _ValidateScript(text, ahkPath) {
        if !ahkPath or !FileExist(ahkPath)
            return
        tag := Format("{:x}", A_TickCount) "-" Format("{:x}", Random(1, 0x7fffffff))
        vFile := A_Temp "\ahk_hack_validate_" tag ".ahk"
        errFile := A_Temp "\ahk_hack_validate_" tag ".err"
        batFile := A_Temp "\ahk_hack_validate_" tag ".bat"
        doneFile := A_Temp "\ahk_hack_validate_" tag ".done"
        try {
            ; #Warn off keeps warning text out of the captured stream so
            ; only real errors drive the throw below.
            FileAppend "#Warn All, Off`n" text, vFile, "UTF-8"
            ; The command line is routed through a batch file because AHK's
            ; Run re-parses nested quotes and can silently drop switches;
            ; inside the batch, cmd.exe receives the arguments verbatim.
            ; Format builds the quoted line (no manual quote escaping), and
            ; the batch is written without a BOM (cmd would otherwise fold
            ; the BOM into the first token).  /ErrorStdOut routes load
            ; errors to the captured stream so the validator never needs a
            ; dialog.
            batLine := Format('"{1}" /validate /ErrorStdOut "{2}" >"{3}" 2>&1'
                , ahkPath, vFile, errFile)
            FileAppend batLine "`n", batFile, "UTF-8-RAW"
            ; The done marker is only written after the validator exits
            ; (cmd runs batch lines sequentially), so it is a reliable
            ; completion signal - process handles/PIDs are not, because
            ; AHK's Run may surface an intermediate shell whose PID is
            ; immediately reused.
            FileAppend Format('echo done >"{1}"`n', doneFile)
                , batFile, "UTF-8-RAW"
            Run(Format('"{1}"', batFile), , "Hide", &vpid)
            ; The capture file is the primary signal: as soon as the error
            ; text lands, kill the whole validator tree (some builds still
            ; block on a dialog after writing it) and throw with the real
            ; parse message.  The done marker means the validator finished
            ; with no captured error: valid text.
            deadline := A_TickCount + 6000
            loop {
                size := FileExist(errFile) ? FileGetSize(errFile) : 0
                if size > 0 {
                    RunWait(Format('taskkill /pid {1} /T /F', vpid)
                        , , "Hide")
                    detail := Trim(FileRead(errFile, "UTF-8"))
                    if StrLen(detail) > 300
                        detail := SubStr(detail, 1, 300) "..."
                    throw Error("script validation failed: " detail, -1)
                }
                if FileExist(doneFile)
                    break
                if A_TickCount >= deadline {
                    ; No error text and no completion marker: kill the tree
                    ; and treat validation as inconclusive - the loader's
                    ; own rc check still guards the real load.
                    RunWait(Format('taskkill /pid {1} /T /F', vpid)
                        , , "Hide")
                    return
                }
                Sleep 20
            }
        } finally {
            ; The validator tree may still be releasing its file handles
            ; when this runs (taskkill returns before handles close), so
            ; cleanup races must never mask the real validation error.
            try {
                if FileExist(vFile)
                    FileDelete(vFile)
                if FileExist(errFile)
                    FileDelete(errFile)
                if FileExist(batFile)
                    FileDelete(batFile)
                if FileExist(doneFile)
                    FileDelete(doneFile)
            } catch {
            }
        }
    }

    static EvalSubprocess(expr) {
        AhkMagic.Init()
        if !(expr is String)
            throw TypeError("expr must be a string", -1)
        if expr = ""
            throw ValueError("expr must not be empty", -1)
        if StrLen(expr) > 4096
            throw ValueError("expr too long", -1)

        tag := Format("{:x}", A_TickCount) "-" Format("{:x}", Random(1, 0x7fffffff))
        scriptFile := A_Temp "\ahk_mcode_eval_" tag ".ahk"
        resultFile := A_Temp "\ahk_mcode_eval_" tag ".txt"

        quote := expr
        script := "try {`n"
            . "    result := (" quote ")`n"
            . "    FileAppend result, `"" resultFile "`", `"UTF-8`"`n"
            . "} catch as e {`n"
            . "    FileAppend `"ERROR: `" e.Message, `"" resultFile "`", `"UTF-8`"`n"
            . "    ExitApp 1`n"
            . "}`n"
        FileAppend script, scriptFile, "UTF-8"
        try {
            ; Compiled exes built from the regular v2 runtime can re-enter
            ; interpreter mode with /script; the SC build does not support it.
            cmd := A_IsCompiled
                ? Format('"{1}" /script /ErrorStdOut "{2}"', A_AhkPath, scriptFile)
                : Format('"{1}" /ErrorStdOut "{2}"', A_AhkPath, scriptFile)
            RunWait cmd, , "Hide"
            if !FileExist(resultFile)
                throw Error("eval child produced no result", -1)
            result := FileRead(resultFile, "UTF-8")
            if RegExMatch(result, "^ERROR: (.*)$", &m)
                throw Error("eval failed: " m[1], -1)
            return result
        } finally {
            try {
                if FileExist(scriptFile)
                    FileDelete(scriptFile)
                if FileExist(resultFile)
                    FileDelete(resultFile)
            } catch {
            }
        }
    }
}

MC_INTERNAL_LOCATOR_X64 := "4157415641554154565755534881ec38030000488bb424a8030000c744242c000000004885c90f94c04885d2410f94c24108c24d85c0410f94c34508d34885f6410f94c2b8010000004508da0f854a0200004c8b9c24a00300004929d10f92c04929d3410f92c24108c2b8020000000f85270200004d8d9100000200b8030000004d39c20f8712020000498d41074c39d00f87000200004801ca4881c10000010031ff4c89c8eb180f1f840000000000488d58014883c0084c39d04889d8777c803c024875ea488d1c02807b018d75e0807b020d75da4c6373034c01f34883c3074839cb72ca4189ff4531f685ff74154a399cf430010000740b49ffc64d39f775ee4589fe4139fe400f95c583ff40410f93c44108ec75134a899cfc3001000042c744bc3000000000ffc74139fe73804489f3ff449c30e974ffffff85ff0f845301000089fb89d883e00383ff040f832202000031ff4531f631c94885c074384a8d1cf44881c3300100004e8d34b44983c6304531ffeb100f1f84000000000049ffc74c39f87410438b2cbe39cd76f04a8b3cfb89e9ebe84885ff0f84f6000000b8050000004981f8000100000f82e900000031c94889d3eb20660f1f4400004c8d71014881c10101000048ffc34c39c14c89f10f87c2000000803c0a4475e0807c0a018975d9807c0a024c75d2807c0a032475cb807c0a042075c441be04000000eb106666662e0f1f8400000000004983c60242807c33fc66752442807c33fd83751c42807c33fe3a751442807c33ff0074386666662e0f1f8400000000004981fe000100000f8473ffffff42807c33fd6675bb42807c33fe8375b342807c33ff3a75ab42803c330075a44c8d72ff4885c97418410fb6040e3dc3000000740e3dcc000000740748ffc975e831c94801d14531ffeb2eb8040000004881c4380300005b5d5f5e415c415d415e415fc3498d5f014983c706b8080000004d39c74989df77d742803c3ae875e44a8d043a486358014801d84883c0054839c875d04d85ff740f4c89fb41803c1ecc740748ffcb75f431db488d83000100004c39c077ae4801d3b804000000eb044883c002807c03fc667515807c03fd83750e807c03fe3a7507807c03ff007427483d000100000f8478ffffff807c03fd6675cd807c03fe8375c6807c03ff3a75bf803c030075b94d39d34d0f42d3498d41104c39d00f866d0100004531f6488d4a064531ffe99100000083e3fc31ff4531f631c9eb1b66666666662e0f1f8400000000004983c6044c39f30f84befdffff428b6cb43039cd760a4a8bbcf43001000089e9428b6cb43439cd760a4a8bbcf43801000089e9428b6cb43839cd760a4a8bbcf44001000089e9428b6cb43c39cd76b14a8bbcf44801000089e9eba54d8d5f014983c70648ffc1b8060000004d39c74d89df0f8795feffff42803c3ae875dd4a8d043a4c6360014c01e04829d04883c0054c39c875c64d8d5f05498d470a4c39c0400f97c5498d47654939c3410f93c34108eb75a741bb050000004d29e34531e442807c21ffe8750d4e632c214b8d2c234c01ed75264f8d2c274983c5064939c50f8375ffffff4f8d2c274983c50b49ffc44d39c576cae960ffffff4c8d4c242c4889d54889d14c89c24d89d0e80f01000085c00f84f90000004c01ed4c01fd498d042c4883c00a48893e4c89760848895e10488946188b44242c89462031c0e9c8fdffff4d89ceeb14498d46014983c6114d39d64989c60f877afeffff42803c324875e542807c32018975dd42807c32025475d542807c32032475cd42807c32041075c542807c32055575bd42807c32065675b542807c32075775ad42807c32084175a542807c320954759d42807c320a41759542807c320b55758d42807c320c41758542807c320d560f8579ffffff42807c320e410f856dffffff4983fe020f8263ffffff42807c320f570f8557ffffff42807c32ffcc0f854bffffff42807c32fecc0f853fffffff4901d6e9c8fdffffb807000000e9f0fcffff0f1f4000498d80000800004839d0480f42d04983c00431c0eb0d662e0f1f84000000000049ffc04939d0772842807c01fc8375f042807c01fe1075e8460fb65401ff458d5ace4180fb3277d8458911b801000000c3"
MC_MEM_SCRIPT_X64 := "5657534883ec204c89c64989ca488b4c24684d85d20f94c04885d2410f94c04108c04d85c9410f94c34508c34885c9410f94c0b8010000004508d80f85c80100008b4424600f57c00f11010f1141100f1141200f1141300f1141400f1141500f1141600f1141700f1181800000000f1181900000000f1181a00000000f1181b00000000f1181c00000000f1181d00000000f1181e00000000f1181f00000004c8d81000100000f1181000100000f1181100100000f1181200100000f1181400100000f1181500100000f1181600100000f1181700100000f1181800100000f1181900100000f1181a00100000f1181b00100000f1181c00100000f1181d00100000f1181e00100000f1181f00100004c8d1d0a0100004c89194c8d1d300100004c8959084c8d1d350100004c8959104c8d1d5a0100004c8959184c8d1d9f0200004c8959204c8d1da40200004c8959284c8d1da90200004c8959304c8d1dae0200004c8959384889890001000048c781080100000c00000049bb00000000b00400004c899910010000c781180100000200000066c7812c01000000000f1181300100004c898940010000898148010000c6814c010000004c898950010000898158010000c6815c010000004c8989600100004885f674048b3eeb0231ff4885f60f94c34889d14c89c24189f841ffd289c131c083f9010f95c108cb740688c801c0eb06ffc7893e31c04883c4205b5f5ec36666666666662e0f1f84000000000048c7414000000000c7414800000000c6414c0048c7415000000000c7415800000000c6415c0048c7416000000000c390b001c3666666662e0f1f84000000000048c7414000000000c7414800000000c6414c0048c7415000000000c7415800000000c6415c0048c7416000000000c390565753488b41404c8b51504c8b4960448b59484c29d04c01d84589c34c39d8410f43c085c074214189c083f808410f92c34889d64c29d64883fe200f92c34408db740d4531dbeb7e4531c0e9e300000083f82073054531dbeb424589c34183e3e031f6666666662e0f1f840000000000410f100432410f104c32100f1104320f114c32104883c6204939f375e34539c30f849d00000041f6c018742a4c89de4589c34183e3f8662e0f1f840000000000498b3c3248893c324883c6084939f375ef4539c3746d4c89c74c89de4883e703741e4c89de66662e0f1f840000000000410fb61c32881c3248ffc648ffcf75f04d29c34983fbfc773a0f1f8000000000450fb61c3244881c32450fb65c320144885c3201450fb65c320244885c3202450fb65c320344885c32034883c6044939f075cd4d01c24c8951504d01c14c8949605b5f5ec366662e0f1f84000000000031c0c3666666662e0f1f84000000000031c0c3666666662e0f1f840000000000488b4150482b4140c30f1f80000000008b4148c3"
MC_INPROC_EVAL_X64 := "4157415641554154565755534881ecf80000004c8ba424b00100004c8bbc24680100004c8b94246001000048c78424e00000000000400048c78424c8000000000000004885c90f94c04885d2410f94c34108c34d85c00f94c04d85c90f94c308c34408db4d85d20f94c008d84d85ff410f94c34883bc2488010000000f94c34408db08c34883bc2498010000000f94c04883bc24a001000000410f94c34108c34d85e4400f94c7b8010000004408df4008df40f6c7010f85b10900004d8b6c2470498b7424604d8b7424684983fd100f92c04d8d5e044d39eb410f97c3488d7e084c39ef0f97c34408db08c3b80a0000000f85760900004c89b424980000004889b424b8000000498b7424784d8bb424900000004889b424c00000004c8d5e084d39eb0f97c3498bb424a0000000488d7ebf4883ffcf400f92c74c89b424a000000049ffc64939f6400f97c54d8b9c24980000004008dd498d5b0448897424504839f30f97c34008fb4008eb0f85fb0800004c895c2470488bbc2480010000488bb42478010000498b8424800000004889842480000000498b8424880000004889442478498d81000100004885ff4989fb4c0f44d8498d99800100004d8db1000300004c89b424d80000004c89b424f00000004885f6490f44f10f85980000000f57c0410f1101410f114110410f114120410f114130410f114140410f114150410f114160410f114170410f118180000000410f118190000000410f1181a0000000410f1181b0000000410f1181c0000000410f1181d0000000410f1181e0000000410f1181f00000004d8b342441c60436034d8b74240841c60436014d8b7424104d85f6740841c70436010000004d8b7424184d891c360f57c00f11030f1143100f1143200f1143300f1143400f1143500f1143600f1143706641833a00741e4531f66666662e0f1f8400000000006643837c7202004d8d760175f3eb034531f648898c24900000004885ff4c899c24880000004889b424b00000000f85e0020000498d890008100048894c24680f57c00f11000f1140100f1140200f1140300f1140400f1140500f1140600f114070498b44242042c6041800498b44242842c6041801498b44243046893418498b4424384e891418498b44245042c7041800010000498b44245842c70418000100004531db4c89d54889e931c00f1f40000fb73c0185ff0f841e02000083ff27743e83ff2274398d779f6683fe1a0f82070100006683ff5f0f84fd0000008d77a56683fee50f87f00000000fb7f781fe800000000f83e10000004883c002ebb1488d2c014883c502440fb7740102664585f6410f94c7664139fe400f94c64408fe753966666666662e0f1f840000000000664183fe60750b66837d020074044883c502440fb775024883c502664585f6410f94c77406664139fe75d54584ff4c8bbc24680100000f856e0100004181fbfe000000775c4489de480faf7424504803742468488d3c014883c7024d89de4c8b9c248000000049893c334c8b5c247841c60433014c8b9c24a00000006641c7043303014d89f34889ef4829cf4829c74883c7fe48d1ef488b442470893c3041ffc34883c502e9cdfeffff4c895c24584c8d3c014531f64c89fdeb100f1f4400000fb77d024883c50241ffc68d779f6683fe1a72ec8d77bf6683fe1a400f92c66683ff5f410f94c34108f375d4440fb7df4181fb80000000410f92c38d77c66683fef6400f92c64184f374b54d39d7742f488b7c245881ffff000000410f92c366837c01fe2e400f95c64484de4989fb4c8bbc24680100000f843afeffffeb1a4c8b5c24584181fbfe0000004c8bbc24680100000f871efeffff448b5c24584c0faf5c24504c035c24684801c1488b8424800000004a890c18488b4424784ac7041800000000488b8424a00000006642c704180000488b442470468934184c8b5c245841ffc3e9cdfdffff4585db4c89d84c8b9c2488000000488bb424b0000000742189c0488b4c2450480fafc84c8b5424684ac7041100000000498b4424404e8914184c894c2458488954246849c707010000000f57c0410f1147084d8b304c894424504989304c8d8424c80000004889f14c89daff94249000000089c5488b8c24c80000004885c97407ff9424a001000083fd010f85e30100004883bc247001000000488b5424500f84e3010000498b442448488b8c2488000000488b3c084885ff0f84de0100004c897424604c8b9c24a8010000488b8424980000008b0407be010000004439d8488b4c24584c8b8424900000004c8b5424700f85b5010000488b8424900100004885c00f94c24c39c0410f94c04108d0488bbc2488000000488bac24b000000075164889e94889faffd0488b4c245883f8010f85a8020000488b9424d800000048899424e80000004c8bb4249800000041c7840e80010000ffffffff488b8424c000000048c7840880010000ffffffff4989940d8001000049c7840d88010000000000004881c100033f0048894c2438488d8424e00000004889442430488d8424f00000004889442428488d8424e8000000488944242048c744244000003f004c8d8424ac0000004889e931d24989d9ff542468488b4c2450488b5424604889114885c00f84d601000041c70700000000418b0c1e41894f04488b8c24b8000000488b0c1949894f0849894f10498d4f204889ca4829da4883fa0f0f87ec010000ba05000000440fb64413fb44884411fb440fb64413fc44884411fc440fb64413fd44884411fd440fb64413fe44884411fe440fb64413ff44884411ff440fb60413448804114883c2064883fa3575b6e9ae010000488b4424504c893041c70702000000e9590200004c893248b80000000063000000498907e9440200004c893241c70708000000e9350200004c8b8c24c00000004a8d2c0f4c8b8c24b80000004e8d340f4c01ef4803bc249800000049b9010000000080ffff4981c1010001004c898c24d0000000eb234c8b4c24784a8b04084989068b074c01ed4d01ee4c01ef48ffc64439d80f84eafdffff4881fe010001000f84de0000003d011000000f83a600000083f80475cc837d000277c6498b064885c074be49b9010000000080ffff4901c14c3b8c24d0000000727c4c8b8c24a000000042803c0807748c468b0410488b8c2480000000488b1408488b8c248801000041b903010000ff9424980100004c8b9c24a80100004c8b5424704c8b842490000000488b4c2458488b5424504885c00f8548ffffff488b44246048890241c70705000000e92201000041c70703000000e916010000488b44246048890241c70709000000e902010000488b442450488b4c246048890841c70706000000e9e9000000488b44246048890241c70707000000e9d50000000f10030f11010f1043100f1141100f1043200f114120418b4f0483f905774aba270000000fa3ca7340498b442448488b043849894750498b44244848833c38000f8481000000490faff54885f674784881fe20010000b820010000480f42c64883fe01751231c9eb4841c747040000000049894710ebb289c281e2fe01000031c94d8b4424484d8b0438450fb604084588440f584d8b4424484d8b0438450fb64408014588440f594883c1024839ca75d0a8017412498b442448488b04380fb604084188440f588b8424ac000000418987c800000031c04881c4f80000005b5d5f5e415c415d415e415fc3"