Build RE baseline and initial Rust workspace

This commit is contained in:
Jan Petykiewicz 2026-04-02 23:11:15 -07:00
commit ffaf155ef0
39 changed files with 5974 additions and 8 deletions

123
docs/README.md Normal file
View file

@ -0,0 +1,123 @@
# RT3 Reverse-Engineering Handbook
This handbook is the project bootstrap for reverse-engineering and rewriting Railroad Tycoon 3.
It is written for future us first: enough structure to resume work quickly, without pretending the
project is already mature.
## Canonical Target
- Canonical executable: `rt3_wineprefix/drive_c/rt3/RT3.exe` (patch 1.06)
- Reference executable: `rt3_wineprefix/drive_c/rt3_105/RT3.exe` (patch 1.05)
- Canonical SHA-256: `01b0d2496cddefd80e7e8678930e00b13eb8607dd4960096f527564f02af36d4`
- Reference SHA-256: `9e96b0695cb722a700f99c8dce498d34da7235e562b1e275bcc1764f8c9b7eb1`
## Documents
- `setup-workstation.md`: toolchain baseline and local environment setup.
- `re-workflow.md`: how to analyze the binary, record findings, and export reusable artifacts.
- `function-map.md`: canonical schema and conventions for function-by-function mapping.
- `control-loop-atlas.md`: curated atlas of top-level loops, gateways, and subsystem handoffs.
## Repo Conventions
- `docs/`: stable project guidance and durable design notes.
- `tools/py/`: committed Python helpers for analysis and validation.
- `artifacts/exports/`: committed derived outputs that can be regenerated.
- Local-only state stays untracked: `.venv/`, Ghidra projects, Rizin databases, crash dumps, and other
bulky/generated working files.
## Current Baseline
The current technical milestone is a repeatable loop-mapping workflow for the 1.06 executable.
Before injection work or deep file-format work, we capture:
- executable hashes and PE metadata
- section layout, imports, and notable strings
- a starter subsystem inventory plus a control-loop atlas
- focused address and string context exports for branch-deepening passes
- a reusable CLI RE kit for branch dossiers where the atlas needs deeper grounding
- a stable curated function ledger in `artifacts/exports/rt3-1.06/function-map.csv`
Current coverage is broad enough to support future sessions without rediscovery, especially in:
- CRT startup and bootstrap handoff
- shell frame, layout, presentation, deferred-message, and frontend overlay flow
- Multiplayer.win UI, chat, session-event, and transport ownership
- map/scenario load and text-export paths
- shared support layers such as intrusive queues, vectors, hashed stores, and tracked heaps
README maintenance rule:
- Keep this section at subsystem level only.
- Do not mirror per-pass function additions here.
- Detailed mapping progress belongs in `artifacts/exports/rt3-1.06/function-map.csv` and the derived branch artifacts under `artifacts/exports/rt3-1.06/`.
Current local tool status:
- Ghidra is installed at `~/software/ghidra`
- `~/software/ghidra/ghidraRun` launches successfully in an interactive shell
- Rizin is installed and available on `PATH`
- `winedbg` works with `rt3_wineprefix`
- RT3 launches under `/opt/wine-stable/bin/wine` when started from `rt3_wineprefix/drive_c/rt3`
## Next Focus
The next milestone is breadth first. The highest-value passes are:
- promote `docs/control-loop-atlas.md` into the primary human-readable artifact for high-level flow
- name and connect the major loop roots and gateways for startup, shell/UI, frame or presentation,
simulation, map/scenario load, input, save/load, and multiplayer/network
- use `export_startup_map.py` and `export_analysis_context.py` to widen breadth around candidate loop
dispatchers before doing deep leaf naming
- keep the pending-template and multiplayer transport dossiers available, but treat them as targeted
deep-dive tools once a missing atlas edge needs branch-specific grounding
- stand up the Rust workspace so artifacts can be validated in code and a minimal hook DLL can be
built as soon as the 32-bit linker is present
Regenerate the initial exports with:
```bash
python3 tools/py/collect_pe_artifacts.py \
rt3_wineprefix/drive_c/rt3/RT3.exe \
artifacts/exports/rt3-1.06
```
Regenerate the startup-focused Ghidra exports with:
```bash
python3 tools/py/export_startup_map.py \
rt3_wineprefix/drive_c/rt3/RT3.exe \
artifacts/exports/rt3-1.06
```
That default export now walks two roots:
- `entry:0x005a313b`
- `bootstrap:0x00484440`
For a focused branch-deepening pass, regenerate the analysis context exports with:
```bash
python3 tools/py/export_analysis_context.py \
rt3_wineprefix/drive_c/rt3/RT3.exe \
artifacts/exports/rt3-1.06 \
--addr 0x00444dd0 \
--addr 0x00508730 \
--addr 0x00508880 \
--string gpdLabelDB \
--string gpdCityDB \
--string 2DLabel.imb \
--string 2DCity.imb \
--string "Geographic Labels"
```
For the pending-template dispatch-store branch, regenerate the new branch dossier with:
```bash
python3 tools/py/rt3_rekit.py \
pending-template-store \
rt3_wineprefix/drive_c/rt3/RT3.exe \
artifacts/exports/rt3-1.06
```
That dossier is now a targeted follow-up tool, not the default first pass.

View file

@ -0,0 +1,83 @@
# Control-Loop Atlas
This atlas is the high-level map for RT3 1.06. It is intentionally broader than the function map:
each section explains who owns a loop, where it starts, what dispatches it, which state blocks
anchor it, and where control is handed to neighboring subsystems.
## CRT and Process Startup
- Roots: `entry` at `0x005a313b`, CRT helpers in the `0x005a2d..0x005ad4..` range, and `app_bootstrap_main` at `0x00484440`.
- Trigger/Cadence: single process startup path before shell or engine services exist.
- Key Dispatchers: `startup_init_tls_state`, `startup_init_file_handle_table`, `startup_run_init_callback_table`, `__setenvp`, `startup_build_argv`, `___crtGetEnvironmentStringsA`, then `app_bootstrap_main`.
- State Anchors: CRT heap and file-handle tables; process environment and argv storage.
- Subsystem Handoffs: exits the generic CRT path at `app_bootstrap_main`, which becomes the first RT3-owned coordinator.
- Evidence: `artifacts/exports/rt3-1.06/startup-call-chain.md`, `artifacts/exports/rt3-1.06/function-map.csv`.
- Open Questions: exact ownership boundary between compiler CRT shims and the first game-owned bootstrap helper; whether any nontrivial startup callbacks seed game globals before `app_bootstrap_main`.
## Bootstrap and Shell Service Bring-Up
- Roots: `app_bootstrap_main` at `0x00484440` and `bootstrap_init_shell_window_services` at `0x004840e0`.
- Trigger/Cadence: one-time bootstrap handoff immediately after CRT completion.
- Key Dispatchers: `app_bootstrap_main`, `bootstrap_init_shell_window_services`, graphics config load or default-init helpers, runtime capability probes, and early shell service initializers under the `0x004610..0x0053f0..` branch.
- State Anchors: shell service bundle rooted at `0x006d4024`, display/runtime globals under `0x006d4024` and `0x006d4030`, host capability flags gathered by `bootstrap_probe_system_profile`.
- Subsystem Handoffs: hands control into shell/UI configuration, presentation defaults, and later per-frame shell work.
- Evidence: `startup-call-chain.md`, function-map rows for `app_bootstrap_main`, `bootstrap_init_shell_window_services`, `shell_load_graphics_config_or_init_defaults`, `shell_reset_display_runtime_defaults`, and related graphics setup helpers.
- Open Questions: where the first persistent main-loop owner object becomes stable; whether `0x0046c230` or one of the unnamed bootstrap children is the shell's first long-lived coordinator.
## Shell UI Command and Deferred Work Flow
- Roots: shell callback paths that converge on `shell_dispatch_ui_command` at `0x00464410`.
- Trigger/Cadence: event-driven UI command dispatch plus deferred-message queue flushing during shell activity.
- Key Dispatchers: `shell_dispatch_ui_command`, `shell_enqueue_deferred_work_message`, `shell_post_deferred_message_type5`, `shell_post_deferred_message_type6`, `shell_enqueue_deferred_message_type4`, `shell_enqueue_deferred_message_type1`.
- State Anchors: shell object at `0x0062be68`, queue roots around `[this+0x11369d]`, `[this+0x1136a1]`, and `[this+0x1136a5]`, plus global routing gates at `0x006d4034`.
- Subsystem Handoffs: routes into graphics config, scenario-text export, overlay generation, multiplayer UI, and presentation-facing deferred work.
- Evidence: function-map shell rows around `0x00464410` and `0x0051f1d0..0x0051f460`.
- Open Questions: where the deferred queues are drained in the wider frame loop; whether the shell command dispatcher is also used by hotkeys or only by UI callback tables.
## Presentation, Overlay, and Frame Timing
- Roots: shell display runtime setup and the frame-time history path under `shell_update_frame_time_history` at `0x0051fd70`.
- Trigger/Cadence: recurring shell or presentation update loop once the window and display runtime are live.
- Key Dispatchers: `shell_update_frame_time_history`, `shell_set_gamma_ramp_scalar`, overlay builders such as `shell_queue_single_world_anchor_overlay`, `shell_queue_world_anchor_overlay_list`, and `shell_queue_indexed_world_anchor_marker`.
- State Anchors: shell frame history ring at `0x006d403c`, display/runtime state inside the shell block near `[this+0x114282]` and `[this+0x11428a]`, presentation service pointer at `[this+0x0c]`.
- Subsystem Handoffs: consumes world/object state for overlays and pushes deferred presentation messages back through shell queues.
- Evidence: function-map rows for shell frame history, gamma ramp, world-anchor overlay builders, and graphics runtime helpers.
- Open Questions: the exact outer frame pump is still unnamed; simulation cadence and shell presentation cadence may be separate loops that only rendezvous through shared shell state.
## Map and Scenario Content Load
- Roots: reference-database setup via `map_bundle_open_reference_databases` at `0x00444dd0`, followed by narrower loaders such as `map_load_geographic_label_database` and `map_load_city_database`.
- Trigger/Cadence: map or scenario open paths and scenario-text export commands.
- Key Dispatchers: `map_bundle_open_reference_databases`, `map_load_geographic_label_database`, `map_load_city_database`, `scenario_text_export_build_language_file`, `scenario_text_export_report_language_file`, `scenario_text_export_batch_process_maps`.
- State Anchors: map bundle state allocated through `0x00530c80`, geography tables rooted at `0x0062b2fc` and `0x0062b268`, shell-side map-selection context.
- Subsystem Handoffs: feeds shell preview panels, scenario export tooling, and later gameplay map state.
- Evidence: function-map map/scenario rows, analysis-context exports for geographic-label and city database strings.
- Open Questions: the main gameplay map-load entrypoint is still broader than the currently grounded reference-database loaders; scenario load versus scenario-text export remain only partially overlapped.
## Multiplayer Session and Transport Flow
- Roots: Multiplayer.win session-event callback table around `0x00468de0..0x004691d0`, plus the transport object and pending-template paths around `0x00597880..0x0059caf0`.
- Trigger/Cadence: event-driven session callbacks, registered-name updates, and repeated socket I/O service steps.
- Key Dispatchers: session-event wrappers for actions `1`, `2`, `4`, `7`, `8`; `multiplayer_transport_text_stream_service_io`; `multiplayer_transport_dispatch_pending_template_node`; `multiplayer_transport_service_pending_template_dispatch_store`.
- State Anchors: live session globals at `0x006d40d0`, status latch at `0x006cd974`, pending-template list at `[this+0x550]`, dispatch store at `[this+0x55c]`, text-stream buffers at `[this+0x108]` and `[this+0x114]`.
- Subsystem Handoffs: bridges shell/UI status presentation, transport text streams, session state changes, and pending-template completion callbacks.
- Evidence: `function-map.csv`, `pending-template-store-management.md`, `pending-template-store-functions.csv`.
- Open Questions: unresolved request-id semantics for `1`, `2`, `4`, and `7`; the exact owner loop that repeatedly services transport I/O; and how multiplayer preview data ties back into the shell flow.
## Input, Save/Load, and Simulation
- Roots: not yet cleanly named in the current ledger.
- Trigger/Cadence: expected recurring gameplay or shell-driven loops, but the current evidence is still indirect.
- Key Dispatchers: currently unresolved; nearest grounded hints are `bootstrap_capture_keyboard_state`, filesystem-related strings, and shell pathways that save graphics config rather than gameplay state.
- State Anchors: input DLL import `DINPUT8.dll`, save/load path strings under `.\Saved Games\`, and map/scenario globals already observed elsewhere.
- Subsystem Handoffs: likely connect shell/UI into gameplay state, but not yet strongly evidenced in committed exports.
- Evidence: `binary-summary.json`, `imported-dlls.txt`, `subsystem-inventory.md`, and broad strings in `interesting-strings.txt`.
- Open Questions: first real input pump, first gameplay save/load dispatcher, first simulation tick owner, and the exact boundary between shell frame work and gameplay world stepping.
## Next Mapping Passes
- Name the first stable owner of the outer shell or presentation loop above the existing frame-history helpers.
- Connect the map bundle open path to the broader map/scenario load coordinator instead of only the database leaves.
- Identify the owner loop that repeatedly services multiplayer transport I/O and dispatch-store work.
- Ground the first real input ingest path and the first gameplay save/load dispatcher.
- Keep detailed pending-template or transport work scoped to the specific atlas edges that remain unresolved.

75
docs/function-map.md Normal file
View file

@ -0,0 +1,75 @@
# Function Map
The function map is the canonical ledger for reverse-engineering progress. It should be updated even
when the understanding is partial, because partial structure is still useful if it is explicit about
confidence.
The handbook index in `docs/README.md` should stay high-level. Per-pass function additions and
renames belong here and in the derived export artifacts, not in the README. High-level control-loop
and subsystem flow belongs in `docs/control-loop-atlas.md`, with this file staying function-centric.
## Canonical Schema
The committed CSV header is:
```text
address,size,name,subsystem,calling_convention,prototype_status,source_tool,confidence,notes,verified_against
```
Field meanings:
- `address`: virtual address in the canonical 1.06 executable
- `size`: best current size in bytes; leave blank if unknown
- `name`: current best function name
- `subsystem`: coarse ownership bucket such as `startup`, `bootstrap`, `support`, `render`, `audio`, `ui`, `map`, `save`, `network`
- `calling_convention`: `cdecl`, `stdcall`, `thiscall`, `fastcall`, or `unknown`
- `prototype_status`: `unknown`, `inferred`, or `confirmed`
- `source_tool`: primary tool or source behind the current row
- `confidence`: integer from `1` to `5`
- `notes`: short justification, ambiguity, or follow-up
- `verified_against`: hash, runtime trace, second tool, or other corroboration
## Update Rules
- New rows must always include `address`, `name`, `subsystem`, `source_tool`, and `confidence`.
- If a rename is speculative, state that directly in `notes`.
- When two tools disagree on function boundaries, preserve the ambiguity in `notes` instead of hiding it.
- Prefer one row per concrete function, not per guessed feature.
## Starter Subsystems
Use these buckets until the map needs finer structure:
- `startup`
- `bootstrap`
- `shell`
- `support`
- `ui`
- `render`
- `audio`
- `input`
- `network`
- `filesystem`
- `resource`
- `map`
- `scenario`
- `save`
- `simulation`
- `unknown`
## Verification Sources
Examples for `verified_against`:
- `sha256:01b0... + objdump`
- `ghidra + rizin`
- `ghidra + string xref`
- `runtime trace under winedbg`
## Exit Criteria For The Milestone
The first function-mapping milestone is complete when the repo has:
- a stable starter map for the canonical binary
- named anchors for startup and a few obvious subsystem gateways
- enough notes and exports that a future session can continue without rediscovery

217
docs/re-workflow.md Normal file
View file

@ -0,0 +1,217 @@
# Reverse-Engineering Workflow
## Goal
Produce durable, version-safe analysis that first explains high-level control loops and subsystem
handoffs, then feeds the function-by-function Rust rewrite and later DLL-based replacement work.
## Standard Loop
1. Confirm the target binary and record its hash.
2. Refresh the exported baseline artifacts.
3. Update `docs/control-loop-atlas.md` with newly grounded loop roots, dispatchers, cadence points,
state anchors, or subsystem handoffs.
4. Analyze in Ghidra.
5. Cross-check suspicious findings in Rizin or with CLI tools.
6. Update the function map with names, prototypes, ownership, confidence, and loop-relevant notes.
7. Commit regenerated exports and notes that would help future sessions.
## Baseline Export
Use the committed helper:
```bash
python3 tools/py/collect_pe_artifacts.py \
rt3_wineprefix/drive_c/rt3/RT3.exe \
artifacts/exports/rt3-1.06
```
This export pass is expected to produce:
- `binary-summary.json`
- `sections.csv`
- `imported-dlls.txt`
- `imported-functions.csv`
- `interesting-strings.txt`
- `subsystem-inventory.md`
- `function-map.csv`
For the startup-init milestone, run the Ghidra headless export as well:
```bash
python3 tools/py/export_startup_map.py \
rt3_wineprefix/drive_c/rt3/RT3.exe \
artifacts/exports/rt3-1.06
```
Optional flags:
```bash
python3 tools/py/export_startup_map.py \
rt3_wineprefix/drive_c/rt3/RT3.exe \
artifacts/exports/rt3-1.06 \
--depth 2 \
--root entry:0x005a313b \
--root bootstrap:0x00484440
```
This startup pass is expected to add:
- `ghidra-startup-functions.csv`
- `startup-call-chain.md`
The raw CSV now includes root provenance columns:
- `root_name`
- `root_address`
## Context Export
For branch-deepening passes after the initial root mapping, use the committed context exporter:
```bash
python3 tools/py/export_analysis_context.py \
rt3_wineprefix/drive_c/rt3/RT3.exe \
artifacts/exports/rt3-1.06 \
--addr 0x00444dd0 \
--addr 0x00508730 \
--addr 0x00508880 \
--string gpdLabelDB \
--string gpdCityDB \
--string 2DLabel.imb \
--string 2DCity.imb \
--string "Geographic Labels"
```
This pass is expected to add:
- `analysis-context-functions.csv`
- `analysis-context-strings.csv`
- `analysis-context.md`
The function CSV captures target function metadata plus caller callee and data-ref summaries.
The string CSV captures matched strings plus their code or data xrefs.
The Markdown report keeps the human-readable disassembly excerpts that are useful for the next naming pass.
Use this exporter to close missing edges in the atlas before using it for leaf-function refinement.
## Branch RE Kit
For deeper branch work after the atlas identifies a narrow unknown, use the CLI RE kit:
```bash
python3 tools/py/rt3_rekit.py \
pending-template-store \
rt3_wineprefix/drive_c/rt3/RT3.exe \
artifacts/exports/rt3-1.06
```
Optional seed override:
```bash
python3 tools/py/rt3_rekit.py \
pending-template-store \
rt3_wineprefix/drive_c/rt3/RT3.exe \
artifacts/exports/rt3-1.06 \
--seed-addr 0x0059c470 \
--seed-addr 0x0059c540
```
This pass is expected to add:
- `pending-template-store-functions.csv`
- `pending-template-store-record-kinds.csv`
- `pending-template-store-management.md`
The function CSV captures the seed cluster plus adjacent discovered helpers in the same branch.
The record-kinds CSV captures the pending-template dispatch-record destructor switch cases and their
inferred payload cleanup shapes.
The Markdown dossier groups the branch into lifecycle buckets such as init destroy lookup prune and
dispatch.
This branch dossier is intentionally narrower than the atlas. Reach for it only when the broad
loop map is already clear enough that a missing branch blocks the next high-level conclusion.
## Ghidra Workflow
- Create a local project for the canonical 1.06 executable.
- Name the project after the binary version, not just `RT3`, so address notes stay version-safe.
- Import the executable without modifying repo-tracked files.
- Treat Ghidra as the primary source for function boundaries, control flow, and decompilation.
- Local launcher on this host: `~/software/ghidra/ghidraRun`
- Local headless entrypoint on this host: `~/software/ghidra/support/analyzeHeadless`
- Headless project state should live under `ghidra_projects/` and remain untracked.
- The committed wrapper defaults to the `entry` and `bootstrap` roots but can be pointed at additional roots when a milestone needs it.
## Rizin Workflow
Use Rizin as the fast second opinion when you need to:
- check section layout, entrypoints, and imports from the CLI
- confirm function boundaries or calling conventions
- script quick address-oriented inspections without reopening the GUI
## Runtime Debugging
Static analysis comes first. Use `winedbg` only after the local Wine runtime is confirmed to work with
the project prefix and a 32-bit target process. Runtime traces should be recorded back into the
function map as corroborating evidence, not treated as a replacement for static exports.
Current host note:
- `env WINEPREFIX=/home/jan/projects/rrt/rt3_wineprefix winedbg --help` works.
- RT3 launches successfully under `/opt/wine-stable/bin/wine` when the current directory is
`rt3_wineprefix/drive_c/rt3`.
- Launching from the wrong working directory can make the process exit cleanly because the game expects
its relative asset paths to resolve under `C:\\rt3`.
That means runtime work can proceed, but startup commands should always be recorded with the working
directory included.
## Naming Rules
- Names should prefer behavior over implementation detail when behavior is known.
- If behavior is only partly known, keep a neutral prefix such as `subsystem_` or `unk_`.
- Address-derived placeholder names are acceptable, but only as temporary rows.
- Every renamed function should keep a short note explaining why the name is justified.
- For high-level passes, prioritize names that clarify loop role, ownership, or handoff semantics
over names that only describe a local helper's mechanics.
## Confidence Rules
- `1`: address exists, purpose unknown
- `2`: rough subsystem guess only
- `3`: behavior inferred from control flow or strings
- `4`: prototype or side effects mostly understood
- `5`: confirmed by multiple sources or runtime evidence
## Export Policy
Commit exports that are cheap to diff and useful to reuse:
- JSON, CSV, TXT, and Markdown summaries
- function maps and subsystem inventories
- small command outputs that anchor a finding
- raw startup discovery exports from headless Ghidra
Keep these local-only:
- Ghidra projects and caches
- repo-local Ghidra runtime state under `.ghidra/`
- Rizin databases and ephemeral sessions
- temporary dumps and scratch notebooks that have not been curated
Keep the ownership split explicit:
- raw Ghidra or Rizin discovery output is derived data
- `function-map.csv` is the curated ledger and may intentionally diverge from auto-generated names
## Exit Criteria For The Broad-Mapping Milestone
The current breadth-first milestone is complete when the repo has:
- a stable starter map for the canonical binary
- a control-loop atlas covering the major top-level loops and handoff points
- named anchors for startup, shell/UI, frame/presentation, simulation, map/load, input, save/load,
and multiplayer/network flow
- enough notes and exports that a future session can continue without rediscovery

138
docs/setup-workstation.md Normal file
View file

@ -0,0 +1,138 @@
# Workstation Setup
This project targets a Linux host with Wine. The current workspace is a Debian unstable machine with
`python3`, `cargo`, `wine`, `winedbg`, `gdb`, `objdump`, `llvm-objdump`, `strings`, and Java already
present.
## Current Local State
- Ghidra install: `~/software/ghidra`
- Ghidra launcher: `~/software/ghidra/ghidraRun`
- Current Ghidra status: launches successfully in an interactive shell
- Rizin binaries: `/usr/local/bin/rizin`, `/usr/local/bin/rz-bin`, `/usr/local/bin/rz-asm`
- Project Wine prefix: `rt3_wineprefix/`
- Current prefix architecture marker: `win64`
- Preferred Wine runtime: `/opt/wine-stable/bin/wine` (`wine-11.0`)
- Current runtime status: `winedbg` works with the project prefix and RT3 launches under Wine 11 when
started from the install directory
## Required Baseline
- Linux host with Wine capable of running the RT3 install in `rt3_wineprefix/`
- A Wine setup that can run 32-bit Windows targets through the chosen prefix
- Java 21+ for Ghidra
- Python 3.13+ with `venv`
- Rust toolchain for the long-term rewrite, validation CLI, and hook DLL
- Binutils / LLVM CLI tools for quick inspection
- 32-bit MinGW linker support for `i686-pc-windows-gnu`
## Preferred Reverse-Engineering Stack
- Ghidra as the primary GUI disassembler/decompiler
- Rizin as the secondary CLI-first analysis stack
- Existing system tools for quick checks: `objdump`, `llvm-objdump`, `strings`, `gdb`, `winedbg`
## Install Policy
- Prefer upstream Ghidra releases over distro packages on this host.
- Prefer upstream Rizin releases or source builds on this host.
- Do not commit tool project databases or local installs into the repo.
- Commit only exported analysis outputs that can be regenerated.
## Local Python Environment
Create a repo-local virtual environment for committed helper scripts and quick experiments:
```bash
python3 -m venv .venv
source .venv/bin/activate
python -V
```
Start stdlib-only when possible. Add a dependency manifest only when a non-stdlib package becomes
necessary.
## Rust Toolchain
This host uses a user-local Rust install. Source it before running cargo or rustup:
```bash
. ~/.local/share/cargo/env
cargo --version
rustup target list --installed
```
The workspace expects:
- `x86_64-unknown-linux-gnu` for host tools such as `rrt-cli`
- `i686-pc-windows-gnu` for the `rrt-hook` DLL
The current missing piece on this host is the 32-bit linker driver. Install `i686-w64-mingw32-gcc`
and keep the workspace linker config pointed at that binary.
## Suggested Host Layout
- Ghidra install: `~/software/ghidra/`
- Rizin install: system package path such as `/usr/local/bin/`
- Repo-local Python environment: `.venv/`
- Local Ghidra projects: `ghidra_projects/` in the repo root or a sibling workspace
## Basic Verification
These commands should work before starting analysis:
```bash
java -version
/opt/wine-stable/bin/wine --version
objdump --version | head -n 1
llvm-objdump --version | head -n 1
python3 -m venv --help >/dev/null
```
Rust-specific verification:
```bash
. ~/.local/share/cargo/env
cargo test -p rrt-model -p rrt-cli
cargo build -p rrt-hook --target i686-pc-windows-gnu
```
If the hook build fails with `linker i686-w64-mingw32-gcc not found`, the Rust target is installed
but the MinGW PE32 linker is still missing from the host.
For the current end-to-end runtime smoke test, use:
```bash
tools/run_hook_smoke_test.sh
```
That script builds the `dinput8.dll` proxy, copies it into the local RT3 install, and launches RT3
briefly with `WINEDLLOVERRIDES=dinput8=n,b` so Wine prefers the native proxy before the builtin DLL.
`winedbg` is now part of the known-good runtime toolchain for this prefix. Verify it with:
```bash
env WINEPREFIX=/home/jan/projects/rrt/rt3_wineprefix winedbg --help
```
## Launch Pattern
RT3 is sensitive to its working directory because it uses relative paths under `.\Data\`, `.\Maps\`,
and `.\Saved Games\`. Launching it from the repo root can make it start and then exit cleanly without
showing a usable game window.
Use this exact pattern:
```bash
cd /home/jan/projects/rrt/rt3_wineprefix/drive_c/rt3
WINEPREFIX=/home/jan/projects/rrt/rt3_wineprefix /opt/wine-stable/bin/wine RT3.exe
```
If the game appears to fail immediately, check the working directory before assuming a Wine or wow64
regression.
## Canonical Inputs
- Analyze `rt3_wineprefix/drive_c/rt3/RT3.exe` by default.
- Treat `rt3_wineprefix/drive_c/rt3_105/RT3.exe` as a reference build for later diffs.
- Record hashes before trusting any symbol map, address note, or decompilation export.