diff --git a/.github/workflows/modern-tree.yml b/.github/workflows/modern-tree.yml new file mode 100644 index 0000000000..62b2462d53 --- /dev/null +++ b/.github/workflows/modern-tree.yml @@ -0,0 +1,41 @@ +name: modern-tree + +# The C++23 modern tree (rebuild/modern) builds and tests on Linux and Windows x64. +# MODERNIZATION.md: "CI builds Linux ... from step 1 on". No retail data is needed; the retail +# save corpus check (fable_save_check) is run locally. +on: + push: + branches: [main] + paths: ["rebuild/modern/**", ".github/workflows/modern-tree.yml"] + pull_request: + paths: ["rebuild/modern/**", ".github/workflows/modern-tree.yml"] + +jobs: + linux-gcc: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Classic zlib (zlib-ng would break byte-identical saves) + run: sudo apt-get update && sudo apt-get install -y zlib1g-dev ninja-build + # This job links the optional LZO (GPL, downloaded and hash-checked at configure time) so + # both the byte-exact and the stored-form text-blob paths are built; Windows leaves it off. + - name: Configure (with pinned LZO 2.10 download) + run: cmake -S rebuild/modern -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DFABLE_SAVE_WITH_LZO=ON + - name: Build + run: cmake --build build + - name: Test + run: ctest --test-dir build --output-on-failure + + windows-msvc-x64: + runs-on: windows-2022 + steps: + - uses: actions/checkout@v4 + - uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + - name: Configure (pinned zlib 1.3.1 download) + run: cmake -S rebuild/modern -B build -G Ninja -DCMAKE_BUILD_TYPE=Release + - name: Build + run: cmake --build build + - name: Test + run: ctest --test-dir build --output-on-failure diff --git a/docs/journal/2026-10/2026-10-16-modernization-prep.md b/docs/journal/2026-10/2026-10-16-modernization-prep.md new file mode 100644 index 0000000000..e906c749ad --- /dev/null +++ b/docs/journal/2026-10/2026-10-16-modernization-prep.md @@ -0,0 +1,107 @@ +# Modernization prep — 2026-10-16 (Claude) + +User: "Proceed with 64 bit/modernization until asked to stop." This session works the ROADMAP (h) +"can start now" items, which are prerequisites for MODERNIZATION.md steps 1–4. No fork tree, no +game launch, no parity-tree changes. + +## Done + +- **Frame timing** → `docs/modern/FRAME_TIMING.md`. The world already runs fixed-timestep at a + constant **15 world frames/s** (int `0x1375550`, 632 reads / 461 functions, never written), with + render interpolation gated by `IsTimeForServerUpdate` against predicted present time. Retail also + has per-world-frame input event packages + `PeekWorldChecksum`/`CheckSync`: the replay tool the fork + needs already exists in the engine. Uncapped frame rate = audit render-time readers (170 + `GFGetTime` callers), not a new timestep. +- **Persisted types** → `tools/modern/persisted_types.py`, `docs/modern/PERSISTED_TYPES.md/.tsv`. + Saves/defs persist member by member through `CPersistContext::Transfer` / `CPersistTraits`. + 195 leaf types; width hazards: `long`/`unsigned long` (LP64 only), `CWideString` (wchar_t on + Linux), pointer leaves `COpinionDeed *` / `COpinionPersonalityTraits *` (+ list/pair forms) whose + traits must be read before the fork serializer. 388 persisting classes. File loaders outside + CPersistContext (CMap, CLandscapeLayerMesh, CThingManager, CWorld::Load) listed as separate work. +- **D3D9 call-site census** → `tools/modern/d3d9_device_census.py`, + `docs/modern/D3D9_DEVICE_CENSUS.md/.json`. Typed register model from the PDB layouts seeded at the + `CRenderManagerCore*` singleton `[0x1436E18]`, plus a tier B by retail device offset. 252 device sites / 126 functions / 30 of 119 methods (142 tier A, 110 tier B). Lower bound. + +## Findings worth keeping + +- `CShaderRenderManager::D3DDevice` is **+0x1D4 in retail**, +0x1E4 in Ego_r.pdb: the PDB layout is + off by 16 bytes for that class (shader-manager methods push `[this+0x1D4]` as the COM `this` of + SetVertexShaderConstantF/SetPixelShader). Treat `engine/CShaderRenderManager.h` offsets past + ~0x1C0 as suspect until checked. +- `CRenderManagerCore` derives from `CRenderStateManager` (base fills offsets 0..14908); the device + is held twice, at +4 (state manager) and +0x3CF0. +- Untyped field-offset matching is useless for COM classification: `[r+4]`/`[r+8]` mix vertex-buffer + Lock/Unlock with engine virtuals. Only PDB typing or a unique offset is trustworthy. + +## Live session (user permitted the launch) + +- Steam relaunches a debugger-created Fable.exe outside the debugger, so the tracer now attaches to + a normally started game (`--attach`). +- The census seed `[0x1436E18]` was **0 at the frontend**. It is CEngine's copy of the render core, + set in `CEngine::Initialise`. The frontend-live singleton is `[0x13CB508]`, whose +4 and +0x3CF0 + both held the device (confirming the census's two offsets). Both are now census seeds. +- The live device's vtable is a heap copy; QI/Release/CreateAdditionalSwapChain/Reset are + apphelp.dll shims and the other 115 slots point into d3d9.dll. +- Incident: the first attach crashed before detaching and left all 45 game threads suspended. + They were resumed with ResumeThread, and the game recovered. The tracer now detaches in `finally`. + A second bug: pybag `go(timeout)` takes seconds, so 2000 meant 33 minutes. Both are in GOTCHAS. +- Pointer persist leaves resolved from `ego_r.exe`: all deep-serialize, none writes an address + ([PERSISTED_TYPES](../../modern/PERSISTED_TYPES.md)). +- Retail input/event-package recording is compiled in but unreachable: four `.bss` flags + (`0x13B85F5`–`F8`) are only read. Details and the launcher plan are in FRAME_TIMING.md. + +- Live frontend trace (attach, about 33 min because of the timeout bug; capped at 3000 hits per method): + 40 methods. Of 55 exe call sites, 36 are in the static census and 19 are not (Create*, + BeginScene/EndScene, Clear, `CMouseDX::Update` SetCursorPosition, 2D SetVertexDeclaration). 57 more + calls come from inside d3d9.dll. `tools/modern/d3d9_live_report.py` → `docs/modern/D3D9_LIVE_CENSUS.md`. + The game was left running and responsive after detach. +- Static: the world tick can't exceed 15 Hz in retail. Packed raw file-format structs were added + to PERSISTED_TYPES. + +## Open + +- In-game live trace (load a save) for world-render methods. The only static recall gap left on the + frontend trace is the stack-spilled CreateQuery. +- `LeanAngleStepPerFrame` is cleared: it is applied per world frame (WorldUpdate → UpdatePlayerControl + → ControlledMove → ResolveFacingDirection → GFSmoothTowards). Found via Ego_r: the + `LeanAngle` member, then the retail function of the same name. The retail def fields are + Ego_r −4. Next suspects are GUI per-frame tunables and the mouse/camera render path. + +- Census recall vs the live frontend trace is now 54/55 (98%). Levers added: live RTTI globals + (`rtti_live_globals.py` → `ghidra_out/rtti_live_globals.tsv`), static instances and immediate + getters, embedded-struct loads, member pointers (`add r, imm`), COM tail jumps (`[esp+4]` this), + `this` typing from the file's RTTI vtables (11,311 methods), and a dominant-caller rule (≥3 + typed callers, ≥80% agreement; ClearRenderTarget had 5 CDisplayManager callers vs 1 mistyped). + The census now has 341 D3D9 calls, 303 device sites and 45 methods. Still missed: `CreateQuery` in + OnPostSwapScreens 0x9F73C0, via a stack-spilled pointer. +- Header-rule verifier warning: inventory exists (`header_surface.py`); the warning in + `verify_and_land.py` is deferred while parity sessions share that file. + +## Evening: recorder launcher (user: "Game should be free for you now") + +- `tools/modern/recorder_poke.py` arms the compiled-in recorder at the frontend: VirtualAllocEx + a retail `CWideStringData` (16 bytes: vector + NoRefs at +0xC, not the PDB's + wstring), store it in `GEventPackageFileName` 0x13B8660, set flag 0x13B85F7. Starting a world + then runs `InitEventPackageSaving`, which creates the file and writes version `1` (three runs, + `rec*.evp` = 4 bytes each). +- Record format decoded statically ([FRAME_TIMING](../../modern/FRAME_TIMING.md)): `u32 size` plus a `CompressIntoBuffer` + 0x9F19A0 image of the frame's `CGameEventPackageSet`, once per world update. +- Blocked: every world load in the shared install crashed with a plain `Fable.exe` launch, recorder + on or off. That covered New Game twice with the recorder, once without (control), and a + Continue of 1234234. The AV is in `HasPhysicsMesh` 0xA23C40 (null mesh) ← `OnCreate` 0x724010 ← + `LoadSingleThing` (region thing load). Not caused by the recorder. Stopped per the loop rule, and + logged in FINDINGS_LOG. Throwaway profiles `zrec*` were deleted. Fable is closed. +- Exit-catcher recipe: break on the 32-bit `KiUserExceptionDispatcher` / `NtTerminateProcess` / + `ExitProcess` (SysWOW64 export RVA + module base from the attach log) and scan the stack for + Fable return addresses. The stock dbgeng first-chance AV filter did not stop on this one. +- Static, after the live block: every named per-frame tunable is cleared. Screen-message typing runs + in `CPlayerGui::Update` ← `CWorld::UpdateFrame` (retail 0x43A080 ← 0x4A5A40), which Ego_r shows + is driven by the `EAUpdateFrame` game event. The Smack* fields are unused. Sprite rotation is + world step + interpolated draw. `CResponseTimes` is speech ducking, stepped by elapsed world + frames. Only the mouse path is left ([FRAME_TIMING](../../modern/FRAME_TIMING.md)). +- User pointed at the Nexus "Camera Wobble Remover" (.fmp). It is one game.bin entry, + `MOVEMENT_DEFAULT`, with `LeanAngleTurnFactor/LimitAngle/StepPerFrame` set to 0 (stock 60 / 18 / + 3.6, decoded from game.bin.retail-bak entry 7076). The other fields are unchanged. Confirms that + the wobble is the hero lean bending the facing the camera follows. Fork option: a lean-scale + setting. The game was launched briefly to look for the def in memory (not resident at the + frontend) and is still running at the frontend. diff --git a/docs/journal/2026-10/2026-10-17-modernization.md b/docs/journal/2026-10/2026-10-17-modernization.md new file mode 100644 index 0000000000..f67133f676 --- /dev/null +++ b/docs/journal/2026-10/2026-10-17-modernization.md @@ -0,0 +1,361 @@ +# Modernization lane — 2026-10-17 + +User: "Proceed with the 64 bit/modernization until asked to stop". This continues +[2026-10-16-modernization-prep](2026-10-16-modernization-prep.md). The recorder round trip is still +blocked on the shared install's world-load crash, so this session took work that needs no game launch. +Nothing committed. + +## Modern-compiler census (fork steps 1-2 readiness) + +New: `tools/modern/modern_compile_census.py` produces [MODERN_COMPILE_CENSUS](../../modern/MODERN_COMPILE_CENSUS.md) +(+ `.tsv` of every non-clean TU) and `X64_LAYOUT_DRIFT.tsv`. It syntax-checks (`cl /Zs`, VS 2022 +14.38, `/std:c++17 /permissive /Zc:wchar_t-`) every non-test TU under `rebuild/src/compiled/` as +x86 and as x64. A full run takes about 15 minutes at 12 jobs, and `--report` regenerates the report +from `work/modern_census/diagnostics.json`. + +| | x86 (step 1) | x64 (step 2) | +|---|---:|---:| +| clean | 20,199 (99.4%) | 18,079 (88.9%) | +| pointer-truncation warnings only | 0 | 1,594 (7.8%) | +| layout assert only | 115 | 1 | +| code error | 13 | 144 | +| inline asm (x64 has none) | - | 509 (2.5%) | + +Findings: + +- **VC7.1 standard-library layouts break at step 1, not only at x64.** `std::bitset<274>` is + 36 bytes in VC7.1 (32-bit words) and 40 in current MSVC (64-bit words). The `fable_world.h` assert + on it fails in 115 TUs under a modern x86 compiler. Any PDB layout that embeds a VC7.1 container + holds only under VC7.1, so the fork's own types (MODERNIZATION "PDB layouts are 32-bit") are a + step-1 job. +- **126 of the 144 x64 errors are one spelling.** `operator new(unsigned int)` in + `fable_memory_allocator.h` (84 TUs), `fable_file_installer.h` (18), `fable_boot.h` (15) and + `fable_user_profile.h` (9) must take `size_t`. Under VC7.1 `size_t` is `unsigned int`, so the + mangling and the bytes are unchanged. **Not applied:** all four headers have uncommitted edits + from the concurrent native-recreation sessions. Do it when those land, then re-run the parity + gates of the affected TUs. +- The 13 x86 code errors are listed in the report. Two are VC7.1 for-loop scope (`i` used after + its `for`, in the `CEngineSubPrimitiveRenderer` radix sort), and most of the rest are sources whose + methods are missing from their class header (`C2039`). One x64-only error is a local + `typedef unsigned int size_t` (C2371). Two x64 errors are `__asm` in `fable_fpu.h` (C4235). +- x64 pointer-width diagnostics: 7,958 C4312 / 3,248 C4311+C4302 / 95 C4267. **Correction (by + origin, added to the report later): 14,193 of the 14,549 come from 9 lines of `fable_string.h`**, + repeated in the 1,577 TUs that include it. Those lines are the `CWideStringData` + `unknown04`/`unknown08` property shim, which exposes `_M_finish` and `_M_end_of_storage` as + `fable_u32` for code landed before `Data` was modelled, plus three readers that cast them back to + `const wchar_t*`. Retyping that shim to a pointer-width alias (still `unsigned int` under VC7.1, + so no byte change) removes them. Not applied: the header has uncommitted peer edits. What is left + is 311 distinct sites in 118 TUs' own sources (the `unsigned long` cursor idiom of byte-matched + code). +- `--layouts`: of the 1,499 classes with a size assert, 1,045 change size on x64 (vtables and + pointer members). Persistence does not depend on this (member-wise `Transfer`, PERSISTED_TYPES). + +Method gotcha (GOTCHAS): MSVC looks up `#include "rebuild_abi.h"` in the including header's own +directory first, so an overlay `/I` directory is ignored. The tool compiles against a patched +*copy* of `rebuild/include` instead (pointer check and size asserts off for x64; live +`static_assert` for `--layouts`). Hand-written `typedef char X[cond ? 1 : -1]` asserts are rewritten +in the copy as well. + +## Mouse look: stepped per world frame (FRAME_TIMING open item cleared) + +Static, capstone over retail. Details in [FRAME_TIMING](../../modern/FRAME_TIMING.md). + +- `CMouseDX::Update` 0xAB58E0 drains the DirectInput buffer, sums the deltas, optionally averages + them with the previous call (filtering), queues one event per call, and re-centres the cursor. +- `CInputManager::Update` 0x9F57A0 drives mouse, keyboard and pads. In-game it is called only by + `CMainGameComponent::GetInputs` 0x416E78, which `CMainGameComponent::Update` reaches only on a world + frame (`GetGameEventPackageSet` → `IsTimeForServerUpdate`). +- `CInputProcessCameraLookAround::ProcessInput` 0x68D790 *sets* the look vector (0x697B10). With + one event per world frame that loses nothing. **Mouse sensitivity does not depend on render rate.** + The live per-render-frame calls came from the frontend. +- `DIPROP_BUFFERSIZE` = 256 records (set in `CMouseDX::SetExclusiveMode` 0xAB5710). It is drained + once per world frame, so **4000/8000 Hz mice overflow it and lose look motion** (about 530/1,070 + records per 66.7 ms). 1000 Hz fits (about 133). Fork input service: an uncapped raw-input + accumulator, one summed delta per world frame. Noted under FABLE_TLC_REPORTED_BUGS "Camera + micro-stutter at high mouse polling rates" (untested in-game). +- Frontend: `CNewFrontendGameComponent::Run` 0x42EC7C runs Input/Update/Draw per render frame, + but its key and stick repeat uses a real-time `CStopWatch` (0.333 s initial, then 0.085 s). So + the main menu is not the "menu input repeats too rapidly at high frame rates" report. + Annotated in FABLE_TLC_REPORTED_BUGS. + +- `GFGetTime` render-side sweep (185 sites / 101 functions): every site is a real-time threshold, + dt-scaled accumulation, profiling, a seconds-to-world-frames conversion, or a GUI container that + passes real `dt` to its child widgets at render rate (10 functions). None feeds a render-frame + count into state. Table in FRAME_TIMING. Left: the child widgets' `dt` bodies. +- Candidate retail mouse patch (not applied): `DIPROP_BUFFERSIZE` immediates at VA 0xAB57BA / + 0xAB5871, 256 → 8192. The read loop 0xAB4910 already refills until the device is empty. + +## Save container codec: retail saves round-trip byte for byte (step 2 piece) + +New module `rebuild/modern/save/` (C++23, `/W4 /permissive-`, no pointer-width dependence): +`SaveContainerCodec::decode/encode` and `ccrc`, the `fable_save_tests` unit tests and the +`fable_save_check` corpus tool. See its README. + +- The decoder enforces the retail gates exactly (`VerifySignature` 0x409730: magic, bounds, + seed-0 CRC, 0x4B000 slab, zero padding), then the chunk framing. +- **Correction to SAVE.md §2.4.** Retail is zlib 1.2.1. Level 9 / memLevel 8 / default strategy + in upstream 1.2.1 *and* in classic zlib 1.3.1 reproduces retail's compressed bytes. The 2026-07 + "not achievable" result came from Python's zlib, which here is zlib-ng. Parameter sweep: chunk0 + matches levels 7-9 at every tested memLevel except 1, and chunk1 pins level 9 with memLevel 8 (or 9). +- Container layer alone, over the corpus (409 `FableSave!` files in the local save folder): + 404 exact whole-file rewrites, 5 section-only (all written by our Python tools), 0 rejected. + The deeper layers below bring the final verdict to 402 exact. Identical on the x86 and x64 + builds and with the pinned zlib 1.3.1 download (`FetchContent`, SHA-256 checked). +- Layer 2 started: `PersistStream` (tagged binary `CPersistContext` primitives with pinned widths + and retail-style tag checks) and `SaveHeader` (the 23 HEADER fields). **All 409 HEADER sections + decode to typed fields and re-encode byte-identically**, x86 and x64. +- Body layer: `SaveBody` (the game clock `Time`/`TimeStep`/`DayCount` from 0x6BBB30, then named + sections via StartSection/EndSection 0x9BAB60/0x9BABF0) and the typed `PlayerSection`. Two + writer shapes, both present in the corpus: full (291) and minimal `.qs.hs` (113, ENTITIES only). + **All 402 game-written saves pass every layer byte-exactly.** The five failures are all Python + editor backups: three with broken section tiling, and two where ENTITIES is one byte short so the + next section reads `LAYER`. The checker now flags both. +- ENTITIES layer: the exact SAVED_ENTITIES grammar (the head's slot count, `u64 0` empty slots, + `29+clen` records, a 20-byte index table) replaces SAVE.md's anchor walk, which missed 7 of 32 + cells in Manual-Save1, and its per-record marker. Cells are zlib **level 1**. All 1,921 non-empty + cells, and every game-written save end to end, re-encode byte-exactly. SAVE.md §3.6 is updated. +- `Profile.bin`: `Profile` (FablePro container, ordered `Name value;` entries, the save + registry, edit + re-sign). All 153 local profiles re-encode byte-exactly. +- Portability: the same tree builds warning-free with MSVC x86 and x64 (`/W4 /permissive-`) + and with GCC 15 (WinLibs, `-Wall -Wextra -Wconversion -Wpedantic`), and the corpus result is + identical on all three. New CI `.github/workflows/modern-tree.yml` runs Ubuntu 24.04 GCC with + system zlib and Windows MSVC x64 with the pinned zlib on `rebuild/modern/**` changes. It has not + run yet because nothing is pushed. WSL is not installed on this machine. +- The modern CMake project now enables C (for zlib). Build dirs: `work/modern_build_{x86,x64,x64_fetch,gcc}`. + +## Save text layers: NPC names and LZO (QUESTS/FACTIONS) + +- **SAVED_NPC_NAMES** is `CThingManager::NPCNameMap`, `map>` + (PDB member; writer 0x524440). It holds u32 count, then u64 uid, counted group string and + NUL-terminated UTF-16 name per entry, in ascending UID order. An empty group is written as + `UNATTACHED` and read back as empty (0x525A62). The section is empty in exactly the 113 minimal + `.qs.hs` saves (context flag +0x102). All 403 game-written saves parse, and the C++ type + `SavedNpcNames` re-encodes them exactly. +- **LZO is stock.** Blob writer 0x996610 calls `lzo1x_999_compress` (0xC08130: the + `lzo1x_999_compress_internal` level-8 argument set, 0x70000 bytes of work memory) on all but the last 3 + bytes and verifies with `lzo1x_decompress`. Upstream **LZO 2.10** (tarball SHA-256 c0f89294…, + only `lzo1x_9x.c` + `lzo_init.c`) reproduces 8,698/8,700 corpus blobs. The two misses are + `flipquest` store-mode streams. +- Framing correction: `clen` is a signed i16 in the reader (0x995FE0). -1 means a u32 follows, + and ≤ 0 means stored raw. The writer stores `ulen ≤ 3` and incompressible text that way. + `tools/parse_save_entities.py` now reads both forms and writes -1 + u32 above 0x7FFF. +- **Licensing decision** (the user left it to me, wanting source available and modifiable): the + repo stays MIT. `src/Lzo.cpp` has our own LZO1X decoder. Without LZO the writer emits + retail's stored form, a shape retail itself writes. `-DFABLE_SAVE_WITH_LZO=ON` fetches LZO + 2.10 (hash-pinned) for byte-exact output; such a binary is a GPL combined work. No LZO code + is committed. The Linux CI job builds with LZO, Windows without. +- Result: MSVC x86 (no LZO), MSVC x64 (with and without), GCC 15 (with LZO, 0 warnings). Every + build gives 402 exact / 2 sections / 5 malformed backups / 153 profiles. With LZO, "exact" + now covers the text blobs too. + +- **REGIONS** typed too (`Regions.hpp`). CRegion::Name and the three bools AppearOnWorldMap, + CreatureGenerationEnabled and SoundThemesEnabled are each `Transfer("")` (tag 0). Element 0 + is skipped. All 290 sections re-encode exactly, so every body section except the entity + streams inside cells is now typed. + +- **ENTITIES head and dead-entity table named.** The 20-byte prefix is format version 2 plus + `CThingManager::CurrentUniqueID` (writer 0x524390). The PDB offset is the evidence, and the + value exceeds the hero UID in all 290 saves. The SAVED_ENTITIES "index" is + `PersistedDeadEntities` (`CPersistedDeadEntity{UID, GamePersistent, SectionNameCRC}`; only + game-persistent ones are written, by 0x521620). SectionNameCRC is seed 0 over the quest + script name: 7/8 corpus values recovered from FinalAlbion.qst, and 0x321ACEE7 is still unknown. + Both are typed in `Entities.hpp` (`EntitiesHead`, `PersistedDeadEntity`), and the corpus is + unchanged at 402 exact. + +- **Every save field tag is named.** Hashing all 38,672 string literals in Fable.exe (seed-0 + CRC, plus `` index forms) names the QUESTS trailer: `NumAcceptedBoasts`, then + `BoastScriptName`/`BoastId`/`BoastFailed`/`BoastCompleted`. The July notes + record two failed wordlist brutes; the index suffix is what they lacked. It also names the + FACTIONS tags (`NumberOfFactions`, `Faction`) and the last dead-entity section CRC + (`"NULL"`). QuestsSection and FactionsSection are fully typed and still exact on all 290 + saves. + +## Render-rate audit finished (FRAME_TIMING) + +Vtables recovered from retail RTTI (TypeDescriptor → COL → vtable; scratch `vtables.py`). +Every CThing `RenderUpdate` slot is an empty stub, except the sound emitter, which follows its +owner. Environment and camera interpolate between two world-frame states. All 50 NUISystem +`Update(dt)` bodies integrate real dt (automated scan plus spot reads). The render-frame counter ++0x16138 is never read. **No code advances game state per render frame.** The high-fps reports +in FABLE_TLC_REPORTED_BUGS are annotated: what remains is interpolation jitter or world-tick +scheduling, both of which need a live sample. Gotcha: capstone's linear sweep stops at the first +undecodable byte, so set `skipdata` for region scans. + +## Bank archive codec (`rebuild/modern/bank/`, step 2 "bank headers") + +C++23 BIGB codec from BIG.md: header, footer directory, TOCs with the stats header, raw +name/dep bytes, info, and payloads as views into the shared file buffer (no copy of the 533 MB +bank). The writer uses §9.1 minimal repack (original address order, boundary = largest power of +two ≤ max file Align dividing the original start). `fable_bank_check --edit` over the install: +**17/17 byte-identical** (8 retail plus 9 copies/backups). A grow edit in every sub-bank leaves +other entries unchanged and keeps alignment, and grow-then-revert is exact in the 16 aligned +files. The live `textures.big` (texture-upscale lane) is packed unaligned (6,321/6,325 +payloads), so revert is only checked to decode there. MSVC x64, GCC 15 (0 warnings), and MSVC +x86 on the banks up to 250 MB. Read-only use of the install. + +- **Overlap with FableForge `forgecore`** (MIT, same owner). It already covers lev/tng/wld/wad/stb, + defs, lut and qst in C++, and uses the same LZO licence rule (clean-room LZO1X, optional + liblzo2 for 999). Its save reader is read-only and its BIG writer copies payloads, so the fork's + `save/` and `bank/` modules add to it rather than duplicate it. I stopped writing level/world + serializers here; the reuse recommendation is in MODERNIZATION "Open questions". + +- **The x64 inline-asm blocker is exactly the de-bake backlog.** Of the 509 TUs with `__asm`, + 503 are `__declspec(naked)` whole-function bakes and 6 are C++ with an `__asm` block. 494 are in + `rebuild/backlog/asm_bake_list.tsv`, and the other 15 (00/47, 00/59, 00/66, 00/85 and 00/99 + shards, July to August) are not landed manifest rows: orphan sources the census sweeps, which + coverage does not count. No genuine reconstruction needs an asm port for x64. De-baking is the + x64 work. + +- **Step-1 layouts: the PDB engine headers survive a modern x86 compiler.** The census's + `--layouts` now takes `--arch x86`. All 1,505 size-asserted classes in the 1,044 + `rebuild/include/engine/*.h` headers keep their size under VS 2022 x86, with 0 other errors. + The generated headers model library members as opaque byte arrays of their PDB size + (`unsigned char Maps[0x10]; // vector`), so the current library's layouts cannot move + them. The step-1 layout break is only the hand-written shared headers (`std::bitset<274>` in + `fable_world.h`, 115 TUs). The report also gained an "x64 readiness by fix" summary. + +- `tools/modern/mouse_buffer_patch.py` packages the high-polling mouse fix. It is SHA-gated to the + Steam exe, verifies both sites, writes to a separate output path only, and changes 2 bytes + (256 → 8,192 records). Tested on a scratch copy; nothing in the install was touched. In-game + test pending (needs an unshared install and a 4-8 kHz mouse). + +## Live session (user: "You may use the game") + +PR #36 opened (`modern/fork-prep-2026-10-17`, from a `main` worktree): modern-tree CI green +on Ubuntu GCC + LZO and Windows MSVC x64. The docs check flagged one link to a file not yet on +main; fixed in the PR. Then a live session (FRAME_TIMING "Live results"): the world ticks at +exactly 15.000 Hz with 4 renders per world frame at 60 Hz VSync. The mouse buffer overflow is +confirmed live with injected bursts (`DI_BUFFEROVERFLOW` on retail 256) and fixed by 8,192 +(0 overflows, drained in 256-record chunks). The VSync-off measurement is not done (the confirm +dialog timed out under automation). Two probe sessions died at detach and the game had to be +closed; all 563 saves were restored and verified. + +- **x64 header fixes applied (user permission 2026-10-17)**: `operator new(unsigned int)` → + `operator new(size_t)` in `fable_memory_allocator.h` (2), `fable_file_installer.h`, + `fable_boot.h` and `fable_user_profile.h`, and the `fable_string.h` `unknown04`/`unknown08` shim + retyped from `fable_u32` to `size_t`. Proven byte-neutral first: all 428 affected TUs (the 128 + C2821 TUs plus a 300-TU sample of the 1,577 `fable_string.h` TUs) were compiled with VC7.1 + `/O2 /Oy` against before/after copies of `rebuild/include`, and 427/427 that compile give + identical `objdump -d -r` (1 fails both ways). Census rerun: **x64 clean 88.9% → 96.5%**, + errors 144 → 26, pointer warnings 14,549 → 468. Left: 8 C2821 in individual sources + (placement `operator new`), and new C4267s where the class `operator new(size_t)` forwards to + `fable_u32` allocators, a real x64 narrowing the fork must widen. The edited headers also hold + other sessions' uncommitted edits, so they are not committed (scratch verifier + `hdrfix_verify.py`, results in `work/hdrfix/result.json`). + +## The modern tree's first frame (user: "We getting close to recreating this post?") + +The post (2026-10-14, parity lane) showed the first title sprite rendered through +reconstructed VC7.1 code. It was one 256×128 half of the logo, which reads "FAE / THE LOST". The +x64 tree now does it whole. `rebuild/modern/codec` (the LZO1X decoder, moved out of `save/`) and +`rebuild/modern/texture` (GBANK descriptor, mip 0, A8R8G8B8/DXT1/DXT3 to RGBA, PNG) decode both +`FRONTEND_TITLE_0{1,2}_SPRITE` from `frontend.big`. `fable_title_view` composes them and +presents them in a 64-bit window, and `--capture` reads the presented swapchain image back: +"FABLE / The Lost Chapters". The first version used D3D11; the user pointed out that the plan is +D3D12 + Vulkan, so the viewer now has exactly those two backends (`--api d3d12|vulkan`), and +their captures are pixel-identical (`work/title_view/presented_{d3d12,vulkan}.png`). Decoder coverage is +394/394 frontend and 6,319/6,324 textures.big entries; the 5 misses use the unconfirmed formats +0x18/0x23. MSVC x64/x86 and GCC builds, tests green, 0 warnings. The viewer is a pixel check, +not the step-4 RHI. + +- **Correction and the real step-1 result.** The user pointed out that the post came from the + parity session: reconstructed engine code (17 VC7.1-parity renderer functions, + `gate_native_retail_title_raster.py`) drawing the sprite, not new code. So + `tools/modern/modern_title_raster.py` compiles those same 17 sources, the + `NativeRetailTitleRaster_test.cpp` harness and its decoder, all unmodified, with **VS 2022 + x86** (`/O2 /MT /std:c++17 /permissive /Zc:wchar_t-`), runs them on the GPU, and compares with + the VC7.1 run's frames: **8/8 byte-identical** (the 288×160 title raster plus 7 controls, + 46,080 title pixels). This is fork step 1 on real reconstructed rendering code. x64 stops at + the parity tree's own 32-bit guards (`rebuild_abi.h` pointer-size check and the + `fable_surface_types.h` size asserts), i.e. the fork-types job of step 2. + +- **Compression decision: open source where we can.** The MIT LZO1X encoder from FableForge + (the user's program; `src/lzo1x.cpp`, commit a358de0) was ported into `rebuild/modern/codec`. + The save writer now uses it when upstream LZO is not linked, and all 8,698 corpus text blobs + re-encode and decode back. The policy is in MODERNIZATION "Decisions": LZO1X (MIT) or stored + form for retail-loadable files, zstd/LZ4/zlib for the fork's own formats, and GPL LZO only as + an optional byte-exact test. Codec round-trip tests added (5 suites green, MSVC + GCC). + +- **Fork step 2 on reconstructed code: the parity lane's title raster runs as x64.** + `tools/modern/modern_title_raster.py --arch x64` compiles the same 17 reconstructed renderer + sources, unmodified, with VS 2022 x64 against the census's patched header copy (32-bit pointer + check and PDB size asserts off). It runs them through the 64-bit `d3d9.dll`: **8/8 frames + byte-identical to the VC7.1 32-bit build**, including the 46,080-pixel retail title (`FAE / THE + LOST`, the post's frame, now from a 64-bit build). The reconstructed sources needed no change. + Only the test harness did, in script-made copies (parity originals untouched): its hard-coded + `+8` for `CDisplayManager::PRenderManager` becomes `2*sizeof(void*)`, the fake `CTexture` gets + pointer-sized slots, and the raw object storage gets 16-byte alignment. These are exactly the + "PDB layouts are 32-bit" hazard, located outside the engine code. + +- **Behaviour census** (`tools/modern/modern_behavior_census.py` → + [MODERN_BEHAVIOR_CENSUS](../../modern/MODERN_BEHAVIOR_CENSUS.md)). All 376 behaviour tests that + `#include` the reconstructed source they check are built (with that source) and run under VC7.1, + VS 2022 x86 and VS 2022 x64. The modern builds use the census header copy (PDB size asserts off), + `/Zc:forScope-` and `/GS-` to match VC7.1 semantics. **Of 326 VC7.1-passing tests, 308 (94.5%) + pass on x86 and 179 (55%) on x64.** + - x86 misses: 8 behaviour failures, mostly `CThing`/`CWorld`/`CPlayer` code, where VS 2022's + 40-byte `std::bitset<274>` (`CThing::UsingComponents`, +0x20) shifts `Components` and every later + member, the known step-1 layout break, now seen in behaviour. Also 8 compile failures from one + source (`CSymbolMap_ContainerLifetime`, C2995), 1 crash (`CList_InitialiseOffsets`) and 1 + STLport `list` destructor link failure. + - x64 misses: 30 naked asm bakes (C2485, the de-bake backlog), the same 8 C2995, and 104 runtime + failures led by `CWorld` (32), STL containers (11), the memory pools (11) and `CPlayer` (6), + i.e. 32-bit layouts and pointer-sized members, the step-2 types job. + - Mechanics: WinError 740 (installer detection wants elevation for exe names with + "install"/"update") means each test gets a neutral `run.exe`. The census header copy gets + junctions `work/modern_census/{src,tests}` → `rebuild/` so headers' `../../src/...` includes resolve. + +- **Retail-layout bitset fixes step 1's layout break.** `rebuild/modern/compat` gains + `fable::compat::retail_bitset`: std::bitset's interface over VC7.1's storage of ceil(N/32) + 32-bit words (274 bits = 36 bytes), with tests against std::bitset. `modern_behavior_census.py + --retail-bitset` builds the modern configs with `CThing::UsingComponents` switched to it. + **VS 2022 x86: 315/326 VC7.1-passing tests pass (was 308)**, and behaviour failures go 8 → 0. + What is left on x86 is 8 compile failures from one source (`CSymbolMap_ContainerLifetime`, C2995), + 1 failure, 1 crash and 1 STLport link. x64 is unchanged at 179: there the 32-bit layouts dominate. + +- **x64 behaviour failures are mostly test harnesses.** Of the 104 x64 runtime regressions + (VC7.1 pass → x64 fail/crash), 96 have a 32-bit shortcut in their harness (the test plus its + non-engine includes): fixed `unsigned long xStorage[N]` arrays sized for 32-bit objects (96), 24 + of them also with hard-coded byte offsets such as `(unsigned char*)expectedPlayer+0x2c`. The + census report now tags these. A third pattern, `reinterpret_cast(store.bytes+0xa4)` + (a hard-coded 32-bit member offset), tags the rest: **105/105** x64 runtime regressions in the + final run carry a 32-bit harness shortcut, so not one x64 behaviour failure is attributable to + the reconstructed code. (The count moved 104→105 between runs: one test is flaky.) The title-raster case shows the fix pattern (`sizeof`/`2*sizeof(void*)`, + aligned storage), applied in copies, never in the parity tests. + +- **Step 3 begins: the fork's mouse input** (`rebuild/modern/platform`). `MouseAccumulator` + folds every OS report into running sums and the world tick takes one frame, so there's no + buffer to overflow. `RawMouseWin32` feeds it from `WM_INPUT`. Live: the injected 5 × 1,500 bursts + that overflowed retail's DirectInput buffer were all counted (7,533 reports, busiest tick 459, + above retail's 256 limit). + +- **Step 3: the world clock.** `IsTimeForServerUpdate` 0x41674A was read in full. A world frame + is due when `predicted_present × 15 − frame > 1`. `predicted_present` is the next vblank after the + queued frames (`CVsyncCallbackTimer`, 0x4166E2), never earlier than now, minus the game start + time (+0x60). Each Update runs at most one world frame, and the render fraction is + `predicted_present × 15 − frame`. `fable::platform::WorldClock` implements that, and its tests + replay the live sample (15 Hz world, 4 renders per world frame at 60 Hz) and hold 15 Hz at + 144-1000 Hz refresh. + +- **Step 3: keyboard.** Retail `CKeyboardDX::Update` reads a 256-key `GetDeviceState` + snapshot and the buffered `GetDeviceData` transitions each world tick. `KeyboardAccumulator` + plus `RawKeyboardWin32` keep both. Live: 60 injected F24 taps (F24 so nothing else reacts) + gave 120 events and 60 presses. + +- **Step 3: pads.** Retail `CJoystickDX` setup (0xAB6B10): DirectInput range 0..0x10000, dead + zone 2000/10000 on X and Y only (left stick), 64-event buffer. UpdateEvents (0xAB6E40) stores + `2*raw/65536 - 1` (+y down). `Pad`/`XInputPad` give the same stick values from XInput, with + separate triggers (DirectInput folds an Xbox pad's triggers into one axis). Live: the user's Xbox + One Wireless Controller is in XInput slot 0 and at rest reads exactly 0. + +## Open / next + +- Apply the `size_t` fix to the four headers once peer edits land (126 x64 TUs). +- Fork step 1 is now measurable: the 13 code errors plus the bitset layout are the whole x86 + modern-compiler gap for the landed code. +- ~~Frontend input driver 0x129D628~~: it is slot 2 of `CInputManagerDX`'s vtable, called by the frontend `Input` at 0x42E434 once per render frame (FRAME_TIMING). +- Save layers still opaque: the entity/component streams inside cells (the reconstructed + engine's Transfer code, not a new model), SAVED_NPC_NAMES, and the QUESTS/REGIONS/FACTIONS text + (LZ-compressed). `parse_save_entities.py` should switch to the exact slot walk. + (Since done: NPC names, REGIONS and the QUESTS/FACTIONS text are typed. Left: the QUESTS + trailer's field names and the streams inside cells.) diff --git a/docs/modern/D3D9_DEVICE_CENSUS.json b/docs/modern/D3D9_DEVICE_CENSUS.json new file mode 100644 index 0000000000..f183fd6ac7 --- /dev/null +++ b/docs/modern/D3D9_DEVICE_CENSUS.json @@ -0,0 +1,3022 @@ +{ + "source": "tools/modern/d3d9_device_census.py", + "functions_scanned": 48655, + "call_sites": 341, + "out_of_range_slots": 0, + "seed_globals": { + "01436E18": "CRenderManagerCore", + "013CB508": "CRenderManager" + }, + "static_instances": 2629, + "rtti_typed_methods": 8948, + "typed_globals": 87, + "typed_methods": 11311, + "interfaces": { + "IDirect3D9": { + "CheckDeviceFormat": { + "call_sites": 11, + "functions": 11 + }, + "CheckDepthStencilMatch": { + "call_sites": 2, + "functions": 2 + }, + "CheckDeviceMultiSampleType": { + "call_sites": 2, + "functions": 2 + }, + "GetAdapterModeCount": { + "call_sites": 1, + "functions": 1 + }, + "EnumAdapterModes": { + "call_sites": 1, + "functions": 1 + } + }, + "IDirect3DDevice9": { + "SetTexture": { + "call_sites": 116, + "functions": 57 + }, + "SetStreamSource": { + "call_sites": 42, + "functions": 29 + }, + "SetIndices": { + "call_sites": 35, + "functions": 28 + }, + "SetVertexShaderConstantF": { + "call_sites": 34, + "functions": 28 + }, + "DrawPrimitiveUP": { + "call_sites": 9, + "functions": 2 + }, + "SetTransform": { + "call_sites": 6, + "functions": 6 + }, + "DrawIndexedPrimitive": { + "call_sites": 4, + "functions": 4 + }, + "DrawPrimitive": { + "call_sites": 4, + "functions": 4 + }, + "Release": { + "call_sites": 3, + "functions": 3 + }, + "SetPixelShader": { + "call_sites": 2, + "functions": 2 + }, + "Present": { + "call_sites": 2, + "functions": 2 + }, + "BeginScene": { + "call_sites": 2, + "functions": 2 + }, + "EndScene": { + "call_sites": 2, + "functions": 2 + }, + "SetRenderTarget": { + "call_sites": 2, + "functions": 2 + }, + "SetDepthStencilSurface": { + "call_sites": 2, + "functions": 2 + }, + "SetSamplerState": { + "call_sites": 2, + "functions": 2 + }, + "SetTextureStageState": { + "call_sites": 2, + "functions": 2 + }, + "SetRenderState": { + "call_sites": 2, + "functions": 2 + }, + "GetDeviceCaps": { + "call_sites": 2, + "functions": 2 + }, + "DrawIndexedPrimitiveUP": { + "call_sites": 2, + "functions": 2 + }, + "ShowCursor": { + "call_sites": 2, + "functions": 2 + }, + "SetCursorPosition": { + "call_sites": 2, + "functions": 1 + }, + "CreateIndexBuffer": { + "call_sites": 2, + "functions": 2 + }, + "SetVertexShader": { + "call_sites": 1, + "functions": 1 + }, + "SetVertexDeclaration": { + "call_sites": 1, + "functions": 1 + }, + "SetPixelShaderConstantF": { + "call_sites": 1, + "functions": 1 + }, + "CreatePixelShader": { + "call_sites": 1, + "functions": 1 + }, + "Clear": { + "call_sites": 1, + "functions": 1 + }, + "SetGammaRamp": { + "call_sites": 1, + "functions": 1 + }, + "SetViewport": { + "call_sites": 1, + "functions": 1 + }, + "StretchRect": { + "call_sites": 1, + "functions": 1 + }, + "GetDisplayMode": { + "call_sites": 1, + "functions": 1 + }, + "Reset": { + "call_sites": 1, + "functions": 1 + }, + "GetBackBuffer": { + "call_sites": 1, + "functions": 1 + }, + "GetDepthStencilSurface": { + "call_sites": 1, + "functions": 1 + }, + "TestCooperativeLevel": { + "call_sites": 1, + "functions": 1 + }, + "CreateOffscreenPlainSurface": { + "call_sites": 1, + "functions": 1 + }, + "CreateDepthStencilSurface": { + "call_sites": 1, + "functions": 1 + }, + "CreateRenderTarget": { + "call_sites": 1, + "functions": 1 + }, + "CreateTexture": { + "call_sites": 1, + "functions": 1 + }, + "SetFVF": { + "call_sites": 1, + "functions": 1 + }, + "CreateVertexShader": { + "call_sites": 1, + "functions": 1 + }, + "CreateVertexDeclaration": { + "call_sites": 1, + "functions": 1 + }, + "CreateVertexBuffer": { + "call_sites": 1, + "functions": 1 + }, + "SetCursorProperties": { + "call_sites": 1, + "functions": 1 + } + }, + "IDirect3DIndexBuffer9": { + "Release": { + "call_sites": 1, + "functions": 1 + } + }, + "IDirect3DPixelShader9": { + "Release": { + "call_sites": 1, + "functions": 1 + } + }, + "IDirect3DSurface9": { + "Release": { + "call_sites": 3, + "functions": 3 + }, + "AddRef": { + "call_sites": 1, + "functions": 1 + }, + "GetDesc": { + "call_sites": 1, + "functions": 1 + } + }, + "IDirect3DTexture9": { + "Release": { + "call_sites": 3, + "functions": 3 + }, + "GetLevelDesc": { + "call_sites": 1, + "functions": 1 + }, + "GetSurfaceLevel": { + "call_sites": 1, + "functions": 1 + }, + "AddRef": { + "call_sites": 1, + "functions": 1 + } + }, + "IDirect3DVertexDeclaration9": { + "Release": { + "call_sites": 1, + "functions": 1 + } + }, + "IDirect3DVertexShader9": { + "Release": { + "call_sites": 1, + "functions": 1 + } + }, + "IDirect3DVolume9": { + "Release": { + "call_sites": 1, + "functions": 1 + } + }, + "IDirect3DVolumeTexture9": { + "Release": { + "call_sites": 3, + "functions": 2 + }, + "AddRef": { + "call_sites": 2, + "functions": 2 + } + } + }, + "sites": [ + { + "function": "00988020", + "name": "ApplyVertexShader", + "call": "009880A7", + "interface": "IDirect3DDevice9", + "method": "SetVertexShader", + "tier": "B" + }, + { + "function": "00988020", + "name": "ApplyVertexShader", + "call": "009880C5", + "interface": "IDirect3DDevice9", + "method": "SetVertexDeclaration", + "tier": "B" + }, + { + "function": "00988190", + "name": "DisablePixelShaders", + "call": "009881A8", + "interface": "IDirect3DDevice9", + "method": "SetPixelShader", + "tier": "B" + }, + { + "function": "009887B0", + "name": "SetVSMatrixConstantTransposed", + "call": "00988825", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00988840", + "name": "SetVSMatrixConstantTransposed", + "call": "009888D1", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00988A20", + "name": "UpdatePixelShader", + "call": "00988A3A", + "interface": "IDirect3DDevice9", + "method": "SetPixelShader", + "tier": "B" + }, + { + "function": "00988A50", + "name": "UpdateCombinedProjectionTransform", + "call": "00988E04", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00988E30", + "name": "UpdateWorldTransform", + "call": "00988E60", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00988E80", + "name": "UpdateLocalisedSphereMapping", + "call": "009891F8", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989230", + "name": "UpdateObjectSpaceTextureTransform", + "call": "0098945A", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "009894B0", + "name": "UpdateWorldSpaceTextureTransform", + "call": "009894FC", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989540", + "name": "UpdateObjectSpaceFogTransform", + "call": "00989616", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989640", + "name": "UpdateWorldSpaceFogTransform", + "call": "00989688", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "009896D0", + "name": "SetUserVertexShaderConstants", + "call": "009896F3", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989700", + "name": "UpdateAmbient", + "call": "00989743", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989760", + "name": "UpdateAmbient", + "call": "009897A3", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "009897C0", + "name": "UpdateFogColour", + "call": "0098980D", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989910", + "name": "SetShadowFadeTransform", + "call": "0098994C", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989960", + "name": "SetShadowFadeFactors", + "call": "0098999D", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "009899B0", + "name": "SetShadowSpotLightFallloffParameters", + "call": "00989A08", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "009899B0", + "name": "SetShadowSpotLightFallloffParameters", + "call": "00989A47", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989A60", + "name": "SetUserVertexShaderConstant", + "call": "00989AA1", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989AB0", + "name": "SetUserVertexShaderConstant", + "call": "00989AED", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989B00", + "name": "SetUserVertexShaderConstant", + "call": "00989B41", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989B50", + "name": "SetUserVertexShaderConstant", + "call": "00989B8D", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989BA0", + "name": "SetXBoxUserVertexShaderConstant", + "call": "00989BD9", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989BF0", + "name": "UpdatePixelShaderConstants", + "call": "00989C98", + "interface": "IDirect3DDevice9", + "method": "SetPixelShaderConstantF", + "tier": "B" + }, + { + "function": "00989CB0", + "name": "UpdateObjectSpaceLighting", + "call": "00989D6D", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989CB0", + "name": "UpdateObjectSpaceLighting", + "call": "0098A05C", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989CB0", + "name": "UpdateObjectSpaceLighting", + "call": "0098A3F8", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "00989CB0", + "name": "UpdateObjectSpaceLighting", + "call": "0098A4FA", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "0098A540", + "name": "UpdateWorldSpaceLighting", + "call": "0098A5BD", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "0098A540", + "name": "UpdateWorldSpaceLighting", + "call": "0098A6E7", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "0098A540", + "name": "UpdateWorldSpaceLighting", + "call": "0098A737", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "0098A760", + "name": "UpdateLightGlobals", + "call": "0098A783", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "0098A9A0", + "name": "CreatePixelShader", + "call": "0098A9D2", + "interface": "IDirect3DDevice9", + "method": "CreatePixelShader", + "tier": "B" + }, + { + "function": "0098B930", + "name": "SetVSConstants", + "call": "0098B94C", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "0098C470", + "name": "SetXBoxUserVertexShaderConstant", + "call": "0098C4A7", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "0098C4C0", + "name": "SetXBoxUserVertexShaderConstant", + "call": "0098C4F7", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "0098C510", + "name": "SetXBoxUserVertexShaderConstant", + "call": "0098C546", + "interface": "IDirect3DDevice9", + "method": "SetVertexShaderConstantF", + "tier": "B" + }, + { + "function": "009A0CF0", + "name": "AttachTextureToStage", + "call": "009A0D21", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "009A25D0", + "name": "?Render@CStreamingFontBank@@UBEXABVCWideString@@ABVC3DVector@@MABVCRGBColour@@KPBVCTexture@@@Z", + "call": "009A2763", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "009A25D0", + "name": "?Render@CStreamingFontBank@@UBEXABVCWideString@@ABVC3DVector@@MABVCRGBColour@@KPBVCTexture@@@Z", + "call": "009A27A1", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "009A25D0", + "name": "?Render@CStreamingFontBank@@UBEXABVCWideString@@ABVC3DVector@@MABVCRGBColour@@KPBVCTexture@@@Z", + "call": "009A33F7", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "009A25D0", + "name": "?Render@CStreamingFontBank@@UBEXABVCWideString@@ABVC3DVector@@MABVCRGBColour@@KPBVCTexture@@@Z", + "call": "009A3433", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "009BE390", + "name": "DestroyDevice", + "call": "009BE3AD", + "interface": "IDirect3DDevice9", + "method": "Release", + "tier": "B" + }, + { + "function": "009BE420", + "name": "ClearRenderTarget", + "call": "009BE481", + "interface": "IDirect3DDevice9", + "method": "Clear", + "tier": "A" + }, + { + "function": "009BE530", + "name": "IsTextureBitDepthSupported", + "call": "009BE56E", + "interface": "IDirect3D9", + "method": "CheckDeviceFormat", + "tier": "A" + }, + { + "function": "009BE590", + "name": "GetBestSignedTextureFormat", + "call": "009BE5DA", + "interface": "IDirect3D9", + "method": "CheckDeviceFormat", + "tier": "A" + }, + { + "function": "009BE610", + "name": "GetBestOpaqueTextureFormat", + "call": "009BE687", + "interface": "IDirect3D9", + "method": "CheckDeviceFormat", + "tier": "A" + }, + { + "function": "009BE6C0", + "name": "GetBestAlphaTextureFormat", + "call": "009BE71B", + "interface": "IDirect3D9", + "method": "CheckDeviceFormat", + "tier": "A" + }, + { + "function": "009BE750", + "name": "GetBestBooleanAlphaTextureFormat", + "call": "009BE7C3", + "interface": "IDirect3D9", + "method": "CheckDeviceFormat", + "tier": "A" + }, + { + "function": "009BE800", + "name": "IsSupportingCompressedTextures", + "call": "009BE81F", + "interface": "IDirect3D9", + "method": "CheckDeviceFormat", + "tier": "A" + }, + { + "function": "009BE830", + "name": "GetBestCompressedAlphaTextureFormat", + "call": "009BE84F", + "interface": "IDirect3D9", + "method": "CheckDeviceFormat", + "tier": "A" + }, + { + "function": "009BE870", + "name": "GetBestCompressedAlphaTextureFormat", + "call": "009BE88F", + "interface": "IDirect3D9", + "method": "CheckDeviceFormat", + "tier": "A" + }, + { + "function": "009BE8B0", + "name": "GetBestCompressedAlphaTextureFormat", + "call": "009BE8CF", + "interface": "IDirect3D9", + "method": "CheckDeviceFormat", + "tier": "A" + }, + { + "function": "009BE8F0", + "name": "IsValidTextureFormat", + "call": "009BE91B", + "interface": "IDirect3D9", + "method": "CheckDeviceFormat", + "tier": "A" + }, + { + "function": "009BE930", + "name": "IsValidRenderTargetFormat", + "call": "009BE969", + "interface": "IDirect3D9", + "method": "CheckDepthStencilMatch", + "tier": "A" + }, + { + "function": "009BEAA0", + "name": "SetGamma", + "call": "009BEB8F", + "interface": "IDirect3DDevice9", + "method": "SetGammaRamp", + "tier": "A" + }, + { + "function": "009BEBB0", + "name": "FUN_009bebb0", + "call": "009BEC11", + "interface": "IDirect3D9", + "method": "CheckDeviceFormat", + "tier": "A" + }, + { + "function": "009BEBB0", + "name": "FUN_009bebb0", + "call": "009BEC2E", + "interface": "IDirect3D9", + "method": "CheckDepthStencilMatch", + "tier": "A" + }, + { + "function": "009BEEB0", + "name": "SwapScreens", + "call": "009BEECE", + "interface": "IDirect3DDevice9", + "method": "Present", + "tier": "B" + }, + { + "function": "009BEF20", + "name": "BeginRender", + "call": "009BEF29", + "interface": "IDirect3DDevice9", + "method": "BeginScene", + "tier": "A" + }, + { + "function": "009BEF50", + "name": "EndRender", + "call": "009BEF61", + "interface": "IDirect3DDevice9", + "method": "EndScene", + "tier": "A" + }, + { + "function": "009BEF80", + "name": "SetViewport", + "call": "009BF0E3", + "interface": "IDirect3DDevice9", + "method": "SetViewport", + "tier": "B" + }, + { + "function": "009BF160", + "name": "SetRenderTarget", + "call": "009BF1C0", + "interface": "IDirect3DDevice9", + "method": "SetRenderTarget", + "tier": "B" + }, + { + "function": "009BF160", + "name": "SetRenderTarget", + "call": "009BF1D1", + "interface": "IDirect3DDevice9", + "method": "SetDepthStencilSurface", + "tier": "B" + }, + { + "function": "009BF220", + "name": "SetRenderTargetAsBackScreen", + "call": "009BF272", + "interface": "IDirect3DDevice9", + "method": "SetRenderTarget", + "tier": "B" + }, + { + "function": "009BF220", + "name": "SetRenderTargetAsBackScreen", + "call": "009BF286", + "interface": "IDirect3DDevice9", + "method": "SetDepthStencilSurface", + "tier": "B" + }, + { + "function": "009BF2D0", + "name": "FlipToGDISurface", + "call": "009BF30B", + "interface": "IDirect3DDevice9", + "method": "Present", + "tier": "B" + }, + { + "function": "009BF350", + "name": "BlitRenderTargetSurfaceToTexture", + "call": "009BF37A", + "interface": "IDirect3DDevice9", + "method": "EndScene", + "tier": "B" + }, + { + "function": "009BF350", + "name": "BlitRenderTargetSurfaceToTexture", + "call": "009BF3BB", + "interface": "IDirect3DDevice9", + "method": "StretchRect", + "tier": "B" + }, + { + "function": "009BF350", + "name": "BlitRenderTargetSurfaceToTexture", + "call": "009BF452", + "interface": "IDirect3DDevice9", + "method": "BeginScene", + "tier": "B" + }, + { + "function": "009BF600", + "name": "GetClosestMode", + "call": "009BF621", + "interface": "IDirect3DDevice9", + "method": "GetDisplayMode", + "tier": "B" + }, + { + "function": "009BFB30", + "name": "ResetDevice", + "call": "009BFC4D", + "interface": "IDirect3D9", + "method": "CheckDeviceMultiSampleType", + "tier": "A" + }, + { + "function": "009BFB30", + "name": "ResetDevice", + "call": "009BFCFC", + "interface": "IDirect3DDevice9", + "method": "Reset", + "tier": "B" + }, + { + "function": "009BFB30", + "name": "ResetDevice", + "call": "009BFD8D", + "interface": "IDirect3DDevice9", + "method": "GetBackBuffer", + "tier": "B" + }, + { + "function": "009BFB30", + "name": "ResetDevice", + "call": "009BFDE1", + "interface": "IDirect3DDevice9", + "method": "GetDepthStencilSurface", + "tier": "B" + }, + { + "function": "009C00C0", + "name": "ProcessLostDevice", + "call": "009C00CC", + "interface": "IDirect3DDevice9", + "method": "TestCooperativeLevel", + "tier": "B" + }, + { + "function": "009C01C0", + "name": "~CInputManagerDX", + "call": "009C0212", + "interface": "IDirect3DDevice9", + "method": "Release", + "tier": "A" + }, + { + "function": "009C02D0", + "name": "FUN_009c02d0", + "call": "009C0354", + "interface": "IDirect3D9", + "method": "GetAdapterModeCount", + "tier": "A" + }, + { + "function": "009C02D0", + "name": "FUN_009c02d0", + "call": "009C036F", + "interface": "IDirect3D9", + "method": "EnumAdapterModes", + "tier": "A" + }, + { + "function": "009C05C0", + "name": "FUN_009c05c0", + "call": "009C0679", + "interface": "IDirect3D9", + "method": "CheckDeviceMultiSampleType", + "tier": "A" + }, + { + "function": "009C1050", + "name": "SetClientWindow", + "call": "009C1071", + "interface": "IDirect3DDevice9", + "method": "Release", + "tier": "B" + }, + { + "function": "009D9C80", + "name": "DrawBufferedPolys", + "call": "009DA47F", + "interface": "IDirect3DDevice9", + "method": "DrawPrimitiveUP", + "tier": "A" + }, + { + "function": "009D9C80", + "name": "DrawBufferedPolys", + "call": "009DA4AC", + "interface": "IDirect3DDevice9", + "method": "DrawPrimitiveUP", + "tier": "A" + }, + { + "function": "009D9C80", + "name": "DrawBufferedPolys", + "call": "009DA5DD", + "interface": "IDirect3DDevice9", + "method": "DrawPrimitiveUP", + "tier": "A" + }, + { + "function": "009D9C80", + "name": "DrawBufferedPolys", + "call": "009DA764", + "interface": "IDirect3DDevice9", + "method": "DrawPrimitiveUP", + "tier": "A" + }, + { + "function": "009D9C80", + "name": "DrawBufferedPolys", + "call": "009DA795", + "interface": "IDirect3DDevice9", + "method": "DrawPrimitiveUP", + "tier": "A" + }, + { + "function": "009D9C80", + "name": "DrawBufferedPolys", + "call": "009DA8CD", + "interface": "IDirect3DDevice9", + "method": "DrawPrimitiveUP", + "tier": "A" + }, + { + "function": "009DA9F0", + "name": "Render2DDrawList", + "call": "009DB365", + "interface": "IDirect3DDevice9", + "method": "DrawPrimitiveUP", + "tier": "A" + }, + { + "function": "009DA9F0", + "name": "Render2DDrawList", + "call": "009DB396", + "interface": "IDirect3DDevice9", + "method": "DrawPrimitiveUP", + "tier": "A" + }, + { + "function": "009DA9F0", + "name": "Render2DDrawList", + "call": "009DB645", + "interface": "IDirect3DDevice9", + "method": "DrawPrimitiveUP", + "tier": "A" + }, + { + "function": "009F2E20", + "name": "Uninitialise", + "call": "009F2E2D", + "interface": "IDirect3DSurface9", + "method": "Release", + "tier": "A" + }, + { + "function": "009F2E70", + "name": "operator=", + "call": "009F2E7D", + "interface": "IDirect3DSurface9", + "method": "Release", + "tier": "A" + }, + { + "function": "009F2E70", + "name": "operator=", + "call": "009F2EB3", + "interface": "IDirect3DSurface9", + "method": "AddRef", + "tier": "A" + }, + { + "function": "009F2F90", + "name": "GetDimensions", + "call": "009F2F9D", + "interface": "IDirect3DSurface9", + "method": "GetDesc", + "tier": "A" + }, + { + "function": "009F3230", + "name": "InitialiseAsImage", + "call": "009F3282", + "interface": "IDirect3DDevice9", + "method": "CreateOffscreenPlainSurface", + "tier": "A" + }, + { + "function": "009F32A0", + "name": "InitialiseAsDepthBuffer", + "call": "009F32B1", + "interface": "IDirect3DSurface9", + "method": "Release", + "tier": "A" + }, + { + "function": "009F32A0", + "name": "InitialiseAsDepthBuffer", + "call": "009F32F7", + "interface": "IDirect3DDevice9", + "method": "CreateDepthStencilSurface", + "tier": "A" + }, + { + "function": "009F3310", + "name": "InitialiseAsRenderTarget", + "call": "009F336D", + "interface": "IDirect3DDevice9", + "method": "CreateRenderTarget", + "tier": "A" + }, + { + "function": "009F9DB0", + "name": "GetDimensions", + "call": "009F9DC1", + "interface": "IDirect3DTexture9", + "method": "GetLevelDesc", + "tier": "A" + }, + { + "function": "009F9E00", + "name": "GetNoLevels", + "call": "009F9E13", + "interface": "IDirect3DTexture9", + "method": "GetSurfaceLevel", + "tier": "A" + }, + { + "function": "009F9F70", + "name": "Uninitialise", + "call": "009F9F7C", + "interface": "IDirect3DTexture9", + "method": "Release", + "tier": "A" + }, + { + "function": "009FA1C0", + "name": "operator=", + "call": "009FA1CC", + "interface": "IDirect3DTexture9", + "method": "Release", + "tier": "A" + }, + { + "function": "009FA1C0", + "name": "operator=", + "call": "009FA219", + "interface": "IDirect3DTexture9", + "method": "AddRef", + "tier": "A" + }, + { + "function": "009FA280", + "name": "Initialise", + "call": "009FA28C", + "interface": "IDirect3DTexture9", + "method": "Release", + "tier": "A" + }, + { + "function": "009FA280", + "name": "Initialise", + "call": "009FA306", + "interface": "IDirect3DDevice9", + "method": "CreateTexture", + "tier": "A" + }, + { + "function": "00A044E0", + "name": "ForceResendRenderStatesToHardware", + "call": "00A04520", + "interface": "IDirect3DDevice9", + "method": "SetSamplerState", + "tier": "B" + }, + { + "function": "00A044E0", + "name": "ForceResendRenderStatesToHardware", + "call": "00A04538", + "interface": "IDirect3DDevice9", + "method": "SetTextureStageState", + "tier": "B" + }, + { + "function": "00A044E0", + "name": "ForceResendRenderStatesToHardware", + "call": "00A0454B", + "interface": "IDirect3DDevice9", + "method": "SetRenderState", + "tier": "B" + }, + { + "function": "00A04630", + "name": "SetupRenderStates", + "call": "00A04647", + "interface": "IDirect3DDevice9", + "method": "GetDeviceCaps", + "tier": "B" + }, + { + "function": "00A058C0", + "name": "RealiseRenderState", + "call": "00A0590B", + "interface": "IDirect3DDevice9", + "method": "SetSamplerState", + "tier": "B" + }, + { + "function": "00A058C0", + "name": "RealiseRenderState", + "call": "00A05923", + "interface": "IDirect3DDevice9", + "method": "SetTextureStageState", + "tier": "B" + }, + { + "function": "00A058C0", + "name": "RealiseRenderState", + "call": "00A05936", + "interface": "IDirect3DDevice9", + "method": "SetRenderState", + "tier": "B" + }, + { + "function": "00A0A940", + "name": "Init", + "call": "00A0A98C", + "interface": "IDirect3DDevice9", + "method": "GetDeviceCaps", + "tier": "B" + }, + { + "function": "00A0AA30", + "name": "SetVertexFormat", + "call": "00A0AA3E", + "interface": "IDirect3DDevice9", + "method": "SetFVF", + "tier": "B" + }, + { + "function": "00A0AB50", + "name": "DrawCurrentBuffersAsIndexedTris", + "call": "00A0AB88", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "00A0AB50", + "name": "DrawCurrentBuffersAsIndexedTris", + "call": "00A0ABCF", + "interface": "IDirect3DDevice9", + "method": "DrawIndexedPrimitive", + "tier": "B" + }, + { + "function": "00A0ABE0", + "name": "DrawCurrentBuffersAsTris", + "call": "00A0AC18", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "00A0ABE0", + "name": "DrawCurrentBuffersAsTris", + "call": "00A0AC53", + "interface": "IDirect3DDevice9", + "method": "DrawPrimitive", + "tier": "B" + }, + { + "function": "00A0AC60", + "name": "DrawCurrentBuffersAsIndexedTris", + "call": "00A0AC89", + "interface": "IDirect3DDevice9", + "method": "DrawIndexedPrimitive", + "tier": "B" + }, + { + "function": "00A0ACA0", + "name": "DrawCurrentBuffersIndexed", + "call": "00A0ACD8", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "00A0ACA0", + "name": "DrawCurrentBuffersIndexed", + "call": "00A0AD27", + "interface": "IDirect3DDevice9", + "method": "DrawIndexedPrimitive", + "tier": "B" + }, + { + "function": "00A0AD40", + "name": "DrawCurrentBuffersIndexed", + "call": "00A0AD72", + "interface": "IDirect3DDevice9", + "method": "DrawIndexedPrimitive", + "tier": "B" + }, + { + "function": "00A0AE10", + "name": "DrawCurrentBuffersAsTris", + "call": "00A0AE2D", + "interface": "IDirect3DDevice9", + "method": "DrawPrimitive", + "tier": "B" + }, + { + "function": "00A0AE40", + "name": "DrawCurrentBuffersAsPointList", + "call": "00A0AE5A", + "interface": "IDirect3DDevice9", + "method": "DrawPrimitive", + "tier": "B" + }, + { + "function": "00A0AE70", + "name": "DrawCurrentBuffersAsPointList", + "call": "00A0AE8A", + "interface": "IDirect3DDevice9", + "method": "DrawPrimitive", + "tier": "B" + }, + { + "function": "00A0AEA0", + "name": "DrawAsIndexedTris", + "call": "00A0AED8", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "00A0AEA0", + "name": "DrawAsIndexedTris", + "call": "00A0AF26", + "interface": "IDirect3DDevice9", + "method": "DrawIndexedPrimitiveUP", + "tier": "B" + }, + { + "function": "00A0AF40", + "name": "DrawAsIndexedTris", + "call": "00A0AF70", + "interface": "IDirect3DDevice9", + "method": "DrawIndexedPrimitiveUP", + "tier": "B" + }, + { + "function": "00A0B0A0", + "name": "SetViewTransform", + "call": "00A0B128", + "interface": "IDirect3DDevice9", + "method": "SetTransform", + "tier": "B" + }, + { + "function": "00A0B140", + "name": "SetViewTransform", + "call": "00A0B1CB", + "interface": "IDirect3DDevice9", + "method": "SetTransform", + "tier": "B" + }, + { + "function": "00A0B1E0", + "name": "SetWorldTransform", + "call": "00A0B271", + "interface": "IDirect3DDevice9", + "method": "SetTransform", + "tier": "B" + }, + { + "function": "00A0B280", + "name": "SetTextureTransform", + "call": "00A0B312", + "interface": "IDirect3DDevice9", + "method": "SetTransform", + "tier": "B" + }, + { + "function": "00A0B320", + "name": "SetPerspectiveTransform", + "call": "00A0B3CE", + "interface": "IDirect3DDevice9", + "method": "SetTransform", + "tier": "B" + }, + { + "function": "00A0B3E0", + "name": "SetPerspectiveTransform", + "call": "00A0B474", + "interface": "IDirect3DDevice9", + "method": "SetTransform", + "tier": "B" + }, + { + "function": "00A0B480", + "name": "BeginRender", + "call": "00A0B4B4", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "B" + }, + { + "function": "00A5ED00", + "name": "`scalar_deleting_destructor'", + "call": "00A5ED13", + "interface": "IDirect3DPixelShader9", + "method": "Release", + "tier": "A" + }, + { + "function": "00A63020", + "name": "CVertexShader", + "call": "00A63055", + "interface": "IDirect3DDevice9", + "method": "CreateVertexShader", + "tier": "A" + }, + { + "function": "00A63020", + "name": "CVertexShader", + "call": "00A63068", + "interface": "IDirect3DDevice9", + "method": "CreateVertexDeclaration", + "tier": "A" + }, + { + "function": "00A630D0", + "name": "`vector_deleting_destructor'", + "call": "00A630E3", + "interface": "IDirect3DVertexShader9", + "method": "Release", + "tier": "A" + }, + { + "function": "00A630D0", + "name": "`vector_deleting_destructor'", + "call": "00A630F0", + "interface": "IDirect3DVertexDeclaration9", + "method": "Release", + "tier": "A" + }, + { + "function": "00A63150", + "name": "Initialise", + "call": "00A63188", + "interface": "IDirect3DDevice9", + "method": "CreateVertexBuffer", + "tier": "A" + }, + { + "function": "00A67460", + "name": "ReleaseVolume", + "call": "00A6746D", + "interface": "IDirect3DVolume9", + "method": "Release", + "tier": "A" + }, + { + "function": "00A67F60", + "name": "ReleaseTexture", + "call": "00A67F6D", + "interface": "IDirect3DVolumeTexture9", + "method": "AddRef", + "tier": "A" + }, + { + "function": "00A67F60", + "name": "ReleaseTexture", + "call": "00A67F76", + "interface": "IDirect3DVolumeTexture9", + "method": "Release", + "tier": "A" + }, + { + "function": "00A67F60", + "name": "ReleaseTexture", + "call": "00A67FD8", + "interface": "IDirect3DVolumeTexture9", + "method": "Release", + "tier": "A" + }, + { + "function": "00A68060", + "name": "Uninitialise", + "call": "00A6806D", + "interface": "IDirect3DVolumeTexture9", + "method": "AddRef", + "tier": "A" + }, + { + "function": "00A68060", + "name": "Uninitialise", + "call": "00A68076", + "interface": "IDirect3DVolumeTexture9", + "method": "Release", + "tier": "A" + }, + { + "function": "00AB5390", + "name": "?SetMouseCursor@CMouseDX@@UAEXABVCSurface@@@Z", + "call": "00AB53E2", + "interface": "IDirect3DDevice9", + "method": "SetCursorProperties", + "tier": "A" + }, + { + "function": "00AB5390", + "name": "?SetMouseCursor@CMouseDX@@UAEXABVCSurface@@@Z", + "call": "00AB53F5", + "interface": "IDirect3DDevice9", + "method": "ShowCursor", + "tier": "A" + }, + { + "function": "00AB58E0", + "name": "?Update@CMouseDX@@UAEXXZ", + "call": "00AB5C8A", + "interface": "IDirect3DDevice9", + "method": "SetCursorPosition", + "tier": "A" + }, + { + "function": "00AB58E0", + "name": "?Update@CMouseDX@@UAEXXZ", + "call": "00AB5CCD", + "interface": "IDirect3DDevice9", + "method": "SetCursorPosition", + "tier": "A" + }, + { + "function": "00AB5D00", + "name": "CMouseDX", + "call": "00AB5DA5", + "interface": "IDirect3DDevice9", + "method": "ShowCursor", + "tier": "A" + }, + { + "function": "00AB7C20", + "name": "?Render@CStaticFontBank@@UBEXABVCWideString@@ABVC3DVector@@MABVCRGBColour@@KPBVCTexture@@@Z", + "call": "00AB8279", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "B" + }, + { + "function": "00AB7C20", + "name": "?Render@CStaticFontBank@@UBEXABVCWideString@@ABVC3DVector@@MABVCRGBColour@@KPBVCTexture@@@Z", + "call": "00AB8296", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "B" + }, + { + "function": "00AB7C20", + "name": "?Render@CStaticFontBank@@UBEXABVCWideString@@ABVC3DVector@@MABVCRGBColour@@KPBVCTexture@@@Z", + "call": "00AB82CC", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "B" + }, + { + "function": "00AB7C20", + "name": "?Render@CStaticFontBank@@UBEXABVCWideString@@ABVC3DVector@@MABVCRGBColour@@KPBVCTexture@@@Z", + "call": "00AB8334", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "B" + }, + { + "function": "00AB7C20", + "name": "?Render@CStaticFontBank@@UBEXABVCWideString@@ABVC3DVector@@MABVCRGBColour@@KPBVCTexture@@@Z", + "call": "00AB8351", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "B" + }, + { + "function": "00AB7C20", + "name": "?Render@CStaticFontBank@@UBEXABVCWideString@@ABVC3DVector@@MABVCRGBColour@@KPBVCTexture@@@Z", + "call": "00AB8383", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "B" + }, + { + "function": "00AC1EE0", + "name": "ReleaseVolume", + "call": "00AC1EED", + "interface": "IDirect3DIndexBuffer9", + "method": "Release", + "tier": "A" + }, + { + "function": "00AC1F70", + "name": "Initialise", + "call": "00AC1F9C", + "interface": "IDirect3DDevice9", + "method": "CreateIndexBuffer", + "tier": "A" + }, + { + "function": "00AC2010", + "name": "CIndexBuffer", + "call": "00AC204F", + "interface": "IDirect3DDevice9", + "method": "CreateIndexBuffer", + "tier": "A" + }, + { + "function": "00B25D80", + "name": "?Uninitialise@CEngine@@UAEXXZ", + "call": "00B25DB7", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B25D80", + "name": "?Uninitialise@CEngine@@UAEXXZ", + "call": "00B25DD2", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B25D80", + "name": "?Uninitialise@CEngine@@UAEXXZ", + "call": "00B25DF8", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B25D80", + "name": "?Uninitialise@CEngine@@UAEXXZ", + "call": "00B25E38", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B25D80", + "name": "?Uninitialise@CEngine@@UAEXXZ", + "call": "00B25E78", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B25D80", + "name": "?Uninitialise@CEngine@@UAEXXZ", + "call": "00B25EB8", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B27D90", + "name": "?Render@CEngine@@UAEXABVCEngineInterpolationInfo@@K@Z", + "call": "00B27DD6", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B27D90", + "name": "?Render@CEngine@@UAEXABVCEngineInterpolationInfo@@K@Z", + "call": "00B27DE3", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B27D90", + "name": "?Render@CEngine@@UAEXABVCEngineInterpolationInfo@@K@Z", + "call": "00B27E15", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B3B0C0", + "name": "UninitialiseShaders", + "call": "00B3B0EA", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B3B0C0", + "name": "UninitialiseShaders", + "call": "00B3B0F7", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B3B0C0", + "name": "UninitialiseShaders", + "call": "00B3B125", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B4FC40", + "name": "SetupShadowFuncRenderStates", + "call": "00B4FC95", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B4FC40", + "name": "SetupShadowFuncRenderStates", + "call": "00B4FCF8", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B54310", + "name": "RenderMist", + "call": "00B54371", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B54310", + "name": "RenderMist", + "call": "00B543C3", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B54310", + "name": "RenderMist", + "call": "00B54414", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B54310", + "name": "RenderMist", + "call": "00B54466", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B54310", + "name": "RenderMist", + "call": "00B544CE", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B54310", + "name": "RenderMist", + "call": "00B5450D", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "B" + }, + { + "function": "00B54890", + "name": "RenderRain", + "call": "00B54947", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B54890", + "name": "RenderRain", + "call": "00B54993", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B54890", + "name": "RenderRain", + "call": "00B549BD", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B54E30", + "name": "RenderSnow", + "call": "00B55011", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B54E30", + "name": "RenderSnow", + "call": "00B5505D", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B555A0", + "name": "?Render@CEngineWeatherRenderer@@UAEXABVCEngineComponentRenderParams@@@Z", + "call": "00B56400", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B555A0", + "name": "?Render@CEngineWeatherRenderer@@UAEXABVCEngineComponentRenderParams@@@Z", + "call": "00B5641D", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B5A3F0", + "name": "ApplyColourFilter", + "call": "00B5ADDA", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B5A3F0", + "name": "ApplyColourFilter", + "call": "00B5AEE0", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B5AF80", + "name": "RenderColorFilteredQuad", + "call": "00B5B931", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B5AF80", + "name": "RenderColorFilteredQuad", + "call": "00B5BA37", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B5CA70", + "name": "CaptureBackBuffer", + "call": "00B5CF3D", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B5CA70", + "name": "CaptureBackBuffer", + "call": "00B5D049", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B5FAB0", + "name": "CaptureBackBuffer", + "call": "00B6015F", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B5FAB0", + "name": "CaptureBackBuffer", + "call": "00B6026B", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B66190", + "name": "RenderSky", + "call": "00B6624E", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "B" + }, + { + "function": "00B66190", + "name": "RenderSky", + "call": "00B6627B", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B66190", + "name": "RenderSky", + "call": "00B662B8", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B66190", + "name": "RenderSky", + "call": "00B662D8", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B67510", + "name": "PostRenderClearRenderStates", + "call": "00B6752A", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B67510", + "name": "PostRenderClearRenderStates", + "call": "00B67543", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B67510", + "name": "PostRenderClearRenderStates", + "call": "00B67569", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B67510", + "name": "PostRenderClearRenderStates", + "call": "00B675A8", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B67510", + "name": "PostRenderClearRenderStates", + "call": "00B675E7", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B6E010", + "name": "RenderBackgroundPatch", + "call": "00B6E175", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "B" + }, + { + "function": "00B6E010", + "name": "RenderBackgroundPatch", + "call": "00B6E19F", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B6E010", + "name": "RenderBackgroundPatch", + "call": "00B6E1F2", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B6E010", + "name": "RenderBackgroundPatch", + "call": "00B6E21C", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B6FA40", + "name": "RenderSeaMidground", + "call": "00B6FAA8", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B6FA40", + "name": "RenderSeaMidground", + "call": "00B6FAF8", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B6FA40", + "name": "RenderSeaMidground", + "call": "00B6FB48", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B70230", + "name": "RenderSeaBackground", + "call": "00B70297", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B70230", + "name": "RenderSeaBackground", + "call": "00B702E8", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B70230", + "name": "RenderSeaBackground", + "call": "00B70338", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B70230", + "name": "RenderSeaBackground", + "call": "00B706D4", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B70230", + "name": "RenderSeaBackground", + "call": "00B706FE", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B70A90", + "name": "RenderEnvironmentMap", + "call": "00B70C9A", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "00B70A90", + "name": "RenderEnvironmentMap", + "call": "00B70F33", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B70A90", + "name": "RenderEnvironmentMap", + "call": "00B7161A", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B72180", + "name": "CreateDynamicTextures", + "call": "00B72CE0", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B72180", + "name": "CreateDynamicTextures", + "call": "00B72D09", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B72180", + "name": "CreateDynamicTextures", + "call": "00B73079", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B72180", + "name": "CreateDynamicTextures", + "call": "00B730CA", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B72180", + "name": "CreateDynamicTextures", + "call": "00B73279", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B72180", + "name": "CreateDynamicTextures", + "call": "00B732CA", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B72180", + "name": "CreateDynamicTextures", + "call": "00B73484", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B72180", + "name": "CreateDynamicTextures", + "call": "00B73610", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B72180", + "name": "CreateDynamicTextures", + "call": "00B73630", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B751F0", + "name": "RenderIceForeground", + "call": "00B753F2", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B751F0", + "name": "RenderIceForeground", + "call": "00B75442", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B751F0", + "name": "RenderIceForeground", + "call": "00B7548F", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B751F0", + "name": "RenderIceForeground", + "call": "00B75A7C", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B75CA0", + "name": "RenderIceBackground", + "call": "00B75D08", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B75CA0", + "name": "RenderIceBackground", + "call": "00B75D58", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B761D0", + "name": "RenderForeground", + "call": "00B763C4", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B761D0", + "name": "RenderForeground", + "call": "00B76414", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B761D0", + "name": "RenderForeground", + "call": "00B76464", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B761D0", + "name": "RenderForeground", + "call": "00B764B1", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B761D0", + "name": "RenderForeground", + "call": "00B76AD5", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B76BC0", + "name": "RenderBackground", + "call": "00B76C28", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B76BC0", + "name": "RenderBackground", + "call": "00B76D52", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B76BC0", + "name": "RenderBackground", + "call": "00B76E7C", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B77370", + "name": "RenderSeaReflectiveForeground", + "call": "00B77747", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B77370", + "name": "RenderSeaReflectiveForeground", + "call": "00B77798", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B77370", + "name": "RenderSeaReflectiveForeground", + "call": "00B777E9", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B77370", + "name": "RenderSeaReflectiveForeground", + "call": "00B77B5A", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B77E20", + "name": "RenderSeaNonReflectiveForeground", + "call": "00B77E88", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B77E20", + "name": "RenderSeaNonReflectiveForeground", + "call": "00B77ED8", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B77E20", + "name": "RenderSeaNonReflectiveForeground", + "call": "00B780F6", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B783F0", + "name": "?Render@CEngineWaterRenderer@@UAEXABVCEngineComponentRenderParams@@@Z", + "call": "00B79154", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B783F0", + "name": "?Render@CEngineWaterRenderer@@UAEXABVCEngineComponentRenderParams@@@Z", + "call": "00B79196", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B783F0", + "name": "?Render@CEngineWaterRenderer@@UAEXABVCEngineComponentRenderParams@@@Z", + "call": "00B791D8", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B783F0", + "name": "?Render@CEngineWaterRenderer@@UAEXABVCEngineComponentRenderParams@@@Z", + "call": "00B7921A", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B783F0", + "name": "?Render@CEngineWaterRenderer@@UAEXABVCEngineComponentRenderParams@@@Z", + "call": "00B7A7D6", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B783F0", + "name": "?Render@CEngineWaterRenderer@@UAEXABVCEngineComponentRenderParams@@@Z", + "call": "00B7A7F6", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B8B780", + "name": "RenderBillboard", + "call": "00B8C08C", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B8B780", + "name": "RenderBillboard", + "call": "00B8C099", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B8B780", + "name": "RenderBillboard", + "call": "00B8C0C4", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B9AC30", + "name": "?RenderSubPrimitive@CEnginePrimitiveManagerRepeatedStaticMeshes@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00B9B5B5", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B9AC30", + "name": "?RenderSubPrimitive@CEnginePrimitiveManagerRepeatedStaticMeshes@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00B9B6A7", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B9AC30", + "name": "?RenderSubPrimitive@CEnginePrimitiveManagerRepeatedStaticMeshes@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00B9BB99", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00B9AC30", + "name": "?RenderSubPrimitive@CEnginePrimitiveManagerRepeatedStaticMeshes@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00B9BBA6", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00B9AC30", + "name": "?RenderSubPrimitive@CEnginePrimitiveManagerRepeatedStaticMeshes@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00B9BBD3", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B9D670", + "name": "SetupFirstTextures", + "call": "00B9D6B0", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B9D670", + "name": "SetupFirstTextures", + "call": "00B9D705", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B9E5B0", + "name": "RenderMeshPrimitiveBlocksIndexBuffer", + "call": "00B9E62C", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B9E5B0", + "name": "RenderMeshPrimitiveBlocksIndexBuffer", + "call": "00B9E689", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B9E750", + "name": "RenderMeshPrimitiveBlocksDegenerateTriangles", + "call": "00B9E790", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00B9E750", + "name": "RenderMeshPrimitiveBlocksDegenerateTriangles", + "call": "00B9E7DF", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BA1B00", + "name": "DisplayMirroredTextures", + "call": "00BA1EE2", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "00BA1B00", + "name": "DisplayMirroredTextures", + "call": "00BA2252", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "00BA1B00", + "name": "DisplayMirroredTextures", + "call": "00BA22D0", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BA2350", + "name": "SetupRenderModeShadersAndConstants", + "call": "00BA29A5", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BA2350", + "name": "SetupRenderModeShadersAndConstants", + "call": "00BA2A86", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BA2350", + "name": "SetupRenderModeShadersAndConstants", + "call": "00BA2AED", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BA2350", + "name": "SetupRenderModeShadersAndConstants", + "call": "00BA2BA1", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BA2350", + "name": "SetupRenderModeShadersAndConstants", + "call": "00BA2DE4", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BA5260", + "name": "RenderMeshPrimitive", + "call": "00BA5CC0", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BA5260", + "name": "RenderMeshPrimitive", + "call": "00BA5CCD", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00BA5260", + "name": "RenderMeshPrimitive", + "call": "00BA5CF8", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BA5260", + "name": "RenderMeshPrimitive", + "call": "00BA5E17", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BA5260", + "name": "RenderMeshPrimitive", + "call": "00BA5E24", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00BA5260", + "name": "RenderMeshPrimitive", + "call": "00BA5E53", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BA5260", + "name": "RenderMeshPrimitive", + "call": "00BA6139", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BA5260", + "name": "RenderMeshPrimitive", + "call": "00BA6146", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00BA5260", + "name": "RenderMeshPrimitive", + "call": "00BA6173", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BA6240", + "name": "?RenderPrimitiveToShadowBuffer@CEnginePrimitiveManagerStaticMeshesBase@@UAE?AW4ERendered@@AAVCEngineInternalPrimitiveBase@@@Z", + "call": "00BA6702", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BA7DB0", + "name": "FUN_00ba7db0", + "call": "00BA7E1E", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "00BAA870", + "name": "RenderFromSortList", + "call": "00BAB96E", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "00BB2540", + "name": "RenderPolygons", + "call": "00BB301E", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "00BB30A0", + "name": "Render2DClothPrimitive", + "call": "00BB3C57", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BB30A0", + "name": "Render2DClothPrimitive", + "call": "00BB3DD8", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BB3E80", + "name": "RenderClothPrimitive", + "call": "00BB4472", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BB3E80", + "name": "RenderClothPrimitive", + "call": "00BB447F", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00BB3E80", + "name": "RenderClothPrimitive", + "call": "00BB44AA", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BB3E80", + "name": "RenderClothPrimitive", + "call": "00BB4F19", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BB3E80", + "name": "RenderClothPrimitive", + "call": "00BB4F26", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00BB3E80", + "name": "RenderClothPrimitive", + "call": "00BB4F53", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BB8EF0", + "name": "?RenderSubPrimitive@CEnginePrimitiveManagerParticleDecalGroup@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00BB9070", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BBAC60", + "name": "RenderLandscapeDecal", + "call": "00BBADCF", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BBC460", + "name": "?RenderSubPrimitive@CEnginePrimitiveManagerParticleGroup@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00BBCB89", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BBC460", + "name": "?RenderSubPrimitive@CEnginePrimitiveManagerParticleGroup@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00BBCBDB", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BBCC70", + "name": "SetupPixelShader", + "call": "00BBCDB4", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BBE090", + "name": "RenderSingleSpriteTrail", + "call": "00BBE6F9", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BBF4A0", + "name": "?RenderSubPrimitive@CEnginePrimitiveManagerParticleSpriteGroup@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00BBF5AB", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BBF4A0", + "name": "?RenderSubPrimitive@CEnginePrimitiveManagerParticleSpriteGroup@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00BBFE88", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "B" + }, + { + "function": "00BBF4A0", + "name": "?RenderSubPrimitive@CEnginePrimitiveManagerParticleSpriteGroup@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00BBFEB5", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00BC04F0", + "name": "RenderSingleSprite", + "call": "00BC05F0", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BC04F0", + "name": "RenderSingleSprite", + "call": "00BC0EB7", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "B" + }, + { + "function": "00BC04F0", + "name": "RenderSingleSprite", + "call": "00BC0EE4", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00BC25F0", + "name": "?RenderSubPrimitive@CEnginePrimitiveManagerParticleMeshGroup@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00BC2EA3", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "00BC25F0", + "name": "?RenderSubPrimitive@CEnginePrimitiveManagerParticleMeshGroup@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00BC2F39", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00BC3F3D", + "name": "RenderSubPrimitive", + "call": "00BC403B", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BC3F3D", + "name": "RenderSubPrimitive", + "call": "00BC4649", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BC3F3D", + "name": "RenderSubPrimitive", + "call": "00BC4673", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00BCA950", + "name": "RenderSpriteTrail", + "call": "00BCB6AB", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BCA950", + "name": "RenderSpriteTrail", + "call": "00BCB6B8", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00BCA950", + "name": "RenderSpriteTrail", + "call": "00BCB6EB", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BCB800", + "name": "?RenderSubPrimitive@CEnginePrimitiveManagerSpriteTrails@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00BCB9E7", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BCC800", + "name": "RenderWeaponTrail", + "call": "00BCD24A", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "B" + }, + { + "function": "00BCC800", + "name": "RenderWeaponTrail", + "call": "00BCD2C8", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BCC800", + "name": "RenderWeaponTrail", + "call": "00BCD2D5", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00BCC800", + "name": "RenderWeaponTrail", + "call": "00BCD30D", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BD5C90", + "name": "?RenderPrimitiveToShadowBuffer@CEnginePrimitiveManagerAnimatedMeshesBase@@UAE?AW4ERendered@@AAVCEngineInternalPrimitiveBase@@@Z", + "call": "00BD6127", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BD5C90", + "name": "?RenderPrimitiveToShadowBuffer@CEnginePrimitiveManagerAnimatedMeshesBase@@UAE?AW4ERendered@@AAVCEngineInternalPrimitiveBase@@@Z", + "call": "00BD6669", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BE0EB0", + "name": "?RenderSubPrimitive@CEnginePrimitiveManager2DParticleGroup@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00BE1922", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BE0EB0", + "name": "?RenderSubPrimitive@CEnginePrimitiveManager2DParticleGroup@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z", + "call": "00BE1969", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BE6F70", + "name": "Render", + "call": "00BE77FF", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BE6F70", + "name": "Render", + "call": "00BE782C", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00BE6F70", + "name": "Render", + "call": "00BE7951", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BE6F70", + "name": "Render", + "call": "00BE797B", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + }, + { + "function": "00BEACA0", + "name": "Render", + "call": "00BEAD77", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BF4570", + "name": "RenderForeground", + "call": "00BF510D", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BF4570", + "name": "RenderForeground", + "call": "00BF5308", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BF4570", + "name": "RenderForeground", + "call": "00BF5491", + "interface": "IDirect3DDevice9", + "method": "SetTexture", + "tier": "A" + }, + { + "function": "00BF4570", + "name": "RenderForeground", + "call": "00BF54E1", + "interface": "IDirect3DDevice9", + "method": "SetStreamSource", + "tier": "A" + }, + { + "function": "00BF4570", + "name": "RenderForeground", + "call": "00BF550F", + "interface": "IDirect3DDevice9", + "method": "SetIndices", + "tier": "A" + } + ] +} diff --git a/docs/modern/D3D9_DEVICE_CENSUS.md b/docs/modern/D3D9_DEVICE_CENSUS.md new file mode 100644 index 0000000000..9bc1912a48 --- /dev/null +++ b/docs/modern/D3D9_DEVICE_CENSUS.md @@ -0,0 +1,136 @@ +# Direct3D 9 call-site census + +Generated by `tools/modern/d3d9_device_census.py` from the retail `Fable.exe`. Do not edit by +hand; every site is in `D3D9_DEVICE_CENSUS.json`. Sizes the RHI for +[MODERNIZATION](MODERNIZATION.md) step 4. + +Method: capstone over every non-thunk function, with registers typed from the PDB layouts. +Seeds: the render-manager pointers `[0x1436E18]` / `[0x13CB508]`, the static `CSystemManager` +`0x13CA618`, the live RTTI sweep of globals (`ghidra_out/rtti_live_globals.tsv`) and `this` of +every method in a retail RTTI vtable. Types spread into callees (unanimous or >= 80% of >= 3 typed +callers), globals, getters, embedded structs and member pointers. A site counts when the same +load supplies the vtable and the COM `this` (pushed, or stored to `[esp+4]` for a tail jump). +Still a lower bound (stack-spilled pointers are not followed), but against the live frontend +trace it finds 54 of 55 call sites ([D3D9_LIVE_CENSUS](D3D9_LIVE_CENSUS.md)). Method order is +the DirectX SDK `d3d9.h`. + +Scanned 48655 functions; typed 11311 methods and 87 globals. +**IDirect3DDevice9: 303 call sites in 144 functions, 45 of 119 methods.** +Tier A (PDB-typed): 192 device sites; tier B (retail device offset: the unique ++0x3CF0, or a class-only method name from Ego_r.pdb): 111. Tier B covers +`CShaderRenderManager`, whose retail device member is +0x1D4 although Ego_r.pdb says +0x1E4. + +## IDirect3DDevice9 methods in use + +| Slot | Method | Call sites | Functions | +| ---: | --- | ---: | ---: | +| 65 | `SetTexture` | 116 | 57 | +| 100 | `SetStreamSource` | 42 | 29 | +| 104 | `SetIndices` | 35 | 28 | +| 94 | `SetVertexShaderConstantF` | 34 | 28 | +| 83 | `DrawPrimitiveUP` | 9 | 2 | +| 44 | `SetTransform` | 6 | 6 | +| 81 | `DrawPrimitive` | 4 | 4 | +| 82 | `DrawIndexedPrimitive` | 4 | 4 | +| 2 | `Release` | 3 | 3 | +| 7 | `GetDeviceCaps` | 2 | 2 | +| 11 | `SetCursorPosition` | 2 | 1 | +| 12 | `ShowCursor` | 2 | 2 | +| 17 | `Present` | 2 | 2 | +| 27 | `CreateIndexBuffer` | 2 | 2 | +| 37 | `SetRenderTarget` | 2 | 2 | +| 39 | `SetDepthStencilSurface` | 2 | 2 | +| 41 | `BeginScene` | 2 | 2 | +| 42 | `EndScene` | 2 | 2 | +| 57 | `SetRenderState` | 2 | 2 | +| 67 | `SetTextureStageState` | 2 | 2 | +| 69 | `SetSamplerState` | 2 | 2 | +| 84 | `DrawIndexedPrimitiveUP` | 2 | 2 | +| 107 | `SetPixelShader` | 2 | 2 | +| 3 | `TestCooperativeLevel` | 1 | 1 | +| 8 | `GetDisplayMode` | 1 | 1 | +| 10 | `SetCursorProperties` | 1 | 1 | +| 16 | `Reset` | 1 | 1 | +| 18 | `GetBackBuffer` | 1 | 1 | +| 21 | `SetGammaRamp` | 1 | 1 | +| 23 | `CreateTexture` | 1 | 1 | +| 26 | `CreateVertexBuffer` | 1 | 1 | +| 28 | `CreateRenderTarget` | 1 | 1 | +| 29 | `CreateDepthStencilSurface` | 1 | 1 | +| 34 | `StretchRect` | 1 | 1 | +| 36 | `CreateOffscreenPlainSurface` | 1 | 1 | +| 40 | `GetDepthStencilSurface` | 1 | 1 | +| 43 | `Clear` | 1 | 1 | +| 47 | `SetViewport` | 1 | 1 | +| 86 | `CreateVertexDeclaration` | 1 | 1 | +| 87 | `SetVertexDeclaration` | 1 | 1 | +| 89 | `SetFVF` | 1 | 1 | +| 91 | `CreateVertexShader` | 1 | 1 | +| 92 | `SetVertexShader` | 1 | 1 | +| 106 | `CreatePixelShader` | 1 | 1 | +| 109 | `SetPixelShaderConstantF` | 1 | 1 | + +## IDirect3DDevice9 methods not seen + +`QueryInterface`, `AddRef`, `GetAvailableTextureMem`, `EvictManagedResources`, `GetDirect3D`, `GetCreationParameters`, `CreateAdditionalSwapChain`, `GetSwapChain`, `GetNumberOfSwapChains`, `GetRasterStatus`, `SetDialogBoxMode`, `GetGammaRamp`, `CreateVolumeTexture`, `CreateCubeTexture`, `UpdateSurface`, `UpdateTexture`, `GetRenderTargetData`, `GetFrontBufferData`, `ColorFill`, `GetRenderTarget`, `GetTransform`, `MultiplyTransform`, `GetViewport`, `SetMaterial`, `GetMaterial`, `SetLight`, `GetLight`, `LightEnable`, `GetLightEnable`, `SetClipPlane`, `GetClipPlane`, `GetRenderState`, `CreateStateBlock`, `BeginStateBlock`, `EndStateBlock`, `SetClipStatus`, `GetClipStatus`, `GetTexture`, `GetTextureStageState`, `GetSamplerState`, `ValidateDevice`, `SetPaletteEntries`, `GetPaletteEntries`, `SetCurrentTexturePalette`, `GetCurrentTexturePalette`, `SetScissorRect`, `GetScissorRect`, `SetSoftwareVertexProcessing`, `GetSoftwareVertexProcessing`, `SetNPatchMode`, `GetNPatchMode`, `ProcessVertices`, `GetVertexDeclaration`, `GetFVF`, `GetVertexShader`, `GetVertexShaderConstantF`, `SetVertexShaderConstantI`, `GetVertexShaderConstantI`, `SetVertexShaderConstantB`, `GetVertexShaderConstantB`, `GetStreamSource`, `SetStreamSourceFreq`, `GetStreamSourceFreq`, `GetIndices`, `GetPixelShader`, `GetPixelShaderConstantF`, `SetPixelShaderConstantI`, `GetPixelShaderConstantI`, `SetPixelShaderConstantB`, `GetPixelShaderConstantB`, `DrawRectPatch`, `DrawTriPatch`, `DeletePatch`, `CreateQuery` + +Not seen is not proof of unused: see the lower-bound note above. + +## Other Direct3D 9 interfaces + +| Interface | Methods (call sites) | +| --- | --- | +| `IDirect3D9` | `CheckDeviceFormat` (11), `CheckDepthStencilMatch` (2), `CheckDeviceMultiSampleType` (2), `GetAdapterModeCount` (1), `EnumAdapterModes` (1) | +| `IDirect3DIndexBuffer9` | `Release` (1) | +| `IDirect3DPixelShader9` | `Release` (1) | +| `IDirect3DSurface9` | `Release` (3), `AddRef` (1), `GetDesc` (1) | +| `IDirect3DTexture9` | `Release` (3), `GetLevelDesc` (1), `GetSurfaceLevel` (1), `AddRef` (1) | +| `IDirect3DVertexDeclaration9` | `Release` (1) | +| `IDirect3DVertexShader9` | `Release` (1) | +| `IDirect3DVolume9` | `Release` (1) | +| `IDirect3DVolumeTexture9` | `Release` (3), `AddRef` (2) | + +## Functions with the most device calls + +| Function | Name | Device calls | +| --- | --- | ---: | +| `00B72180` | `CreateDynamicTextures` | 9 | +| `00BA5260` | `RenderMeshPrimitive` | 9 | +| `009D9C80` | `DrawBufferedPolys` | 6 | +| `00AB7C20` | `?Render@CStaticFontBank@@UBEXABVCWideString@@ABVC3DVector@@MABVCRGBColour@@KPBVCTexture@@@Z` | 6 | +| `00B25D80` | `?Uninitialise@CEngine@@UAEXXZ` | 6 | +| `00B54310` | `RenderMist` | 6 | +| `00B783F0` | `?Render@CEngineWaterRenderer@@UAEXABVCEngineComponentRenderParams@@@Z` | 6 | +| `00BB3E80` | `RenderClothPrimitive` | 6 | +| `00B67510` | `PostRenderClearRenderStates` | 5 | +| `00B70230` | `RenderSeaBackground` | 5 | +| `00B761D0` | `RenderForeground` | 5 | +| `00B9AC30` | `?RenderSubPrimitive@CEnginePrimitiveManagerRepeatedStaticMeshes@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z` | 5 | +| `00BA2350` | `SetupRenderModeShadersAndConstants` | 5 | +| `00BF4570` | `RenderForeground` | 5 | +| `00989CB0` | `UpdateObjectSpaceLighting` | 4 | +| `009A25D0` | `?Render@CStreamingFontBank@@UBEXABVCWideString@@ABVC3DVector@@MABVCRGBColour@@KPBVCTexture@@@Z` | 4 | +| `00B66190` | `RenderSky` | 4 | +| `00B6E010` | `RenderBackgroundPatch` | 4 | +| `00B751F0` | `RenderIceForeground` | 4 | +| `00B77370` | `RenderSeaReflectiveForeground` | 4 | +| `00BCC800` | `RenderWeaponTrail` | 4 | +| `00BE6F70` | `Render` | 4 | +| `0098A540` | `UpdateWorldSpaceLighting` | 3 | +| `009BF350` | `BlitRenderTargetSurfaceToTexture` | 3 | +| `009BFB30` | `ResetDevice` | 3 | +| `009DA9F0` | `Render2DDrawList` | 3 | +| `00A044E0` | `ForceResendRenderStatesToHardware` | 3 | +| `00A058C0` | `RealiseRenderState` | 3 | +| `00B27D90` | `?Render@CEngine@@UAEXABVCEngineInterpolationInfo@@K@Z` | 3 | +| `00B3B0C0` | `UninitialiseShaders` | 3 | +| `00B54890` | `RenderRain` | 3 | +| `00B6FA40` | `RenderSeaMidground` | 3 | +| `00B70A90` | `RenderEnvironmentMap` | 3 | +| `00B76BC0` | `RenderBackground` | 3 | +| `00B77E20` | `RenderSeaNonReflectiveForeground` | 3 | +| `00B8B780` | `RenderBillboard` | 3 | +| `00BA1B00` | `DisplayMirroredTextures` | 3 | +| `00BBF4A0` | `?RenderSubPrimitive@CEnginePrimitiveManagerParticleSpriteGroup@@UAEXAAVCEngineSubPrimitiveBase@@W4ERenderPassType@@@Z` | 3 | +| `00BC04F0` | `RenderSingleSprite` | 3 | +| `00BC3F3D` | `RenderSubPrimitive` | 3 | diff --git a/docs/modern/D3D9_LIVE_CENSUS.md b/docs/modern/D3D9_LIVE_CENSUS.md new file mode 100644 index 0000000000..94f3d35c65 --- /dev/null +++ b/docs/modern/D3D9_LIVE_CENSUS.md @@ -0,0 +1,63 @@ +# D3D9 device census: live trace + +Generated by `tools/modern/d3d9_live_report.py` from a `d3d9_live_census.py --attach` trace. Do not +edit by hand. Complements the static lower bound in [D3D9_DEVICE_CENSUS](D3D9_DEVICE_CENSUS.md). + +Trace `frontend`: device `0F8089A0`, vtable `0F80B8FC` (a heap copy; QI, Release, +CreateAdditionalSwapChain and Reset are apphelp.dll shims). Each method's breakpoint was removed +after 3000 hits, so hit counts are capped. + +- **40 methods** called live; static census has 54; 5 are live-only: `CreateQuery`, `GetScissorRect`, `GetViewport`, `QueryInterface`, `SetScissorRect`. +- Call sites in Fable.exe: **54 match** the static census, **1 are new** (static recall on this trace 98%). +- 57 call sites return into other modules (d3d9.dll state blocks / D3DX), so the fork's D3D9 shim must also cover device calls the engine makes indirectly. + +## Call sites missing from the static census + +| Method | Call | Function | Hits | +| --- | --- | --- | ---: | +| `CreateQuery` | `009F7554` | `009F73C0` OnPostSwapScreens | 1 | + +## Methods by hits + +| Method | Hits | Sites (exe / other) | +| --- | ---: | --- | +| `AddRef` | 3000 | 0 / 3 | +| `Release` | 3000 | 0 / 3 | +| `TestCooperativeLevel` | 3000 | 1 / 1 | +| `SetCursorPosition` | 3000 | 1 / 0 | +| `Present` | 3000 | 1 / 0 | +| `BeginScene` | 3000 | 1 / 0 | +| `EndScene` | 3000 | 1 / 0 | +| `Clear` | 3000 | 1 / 0 | +| `SetTransform` | 3000 | 3 / 11 | +| `SetViewport` | 3000 | 0 / 2 | +| `GetViewport` | 3000 | 0 / 1 | +| `SetRenderState` | 3000 | 2 / 0 | +| `SetTexture` | 3000 | 6 / 1 | +| `SetSamplerState` | 3000 | 2 / 13 | +| `SetScissorRect` | 3000 | 0 / 2 | +| `GetScissorRect` | 3000 | 0 / 1 | +| `DrawIndexedPrimitiveUP` | 3000 | 2 / 0 | +| `SetVertexDeclaration` | 3000 | 4 / 0 | +| `SetVertexShader` | 3000 | 1 / 0 | +| `SetVertexShaderConstantF` | 3000 | 7 / 0 | +| `SetStreamSource` | 3000 | 2 / 0 | +| `SetIndices` | 3000 | 1 / 0 | +| `SetPixelShader` | 3000 | 2 / 0 | +| `SetPixelShaderConstantF` | 3000 | 1 / 0 | +| `DrawPrimitiveUP` | 606 | 1 / 0 | +| `CreateTexture` | 282 | 1 / 0 | +| `SetTextureStageState` | 240 | 1 / 18 | +| `CreateVertexDeclaration` | 198 | 1 / 0 | +| `CreateVertexShader` | 198 | 1 / 0 | +| `QueryInterface` | 79 | 0 / 1 | +| `CreatePixelShader` | 64 | 1 / 0 | +| `CreateVertexBuffer` | 4 | 1 / 0 | +| `CreateIndexBuffer` | 4 | 1 / 0 | +| `GetDeviceCaps` | 2 | 2 / 0 | +| `Reset` | 1 | 1 / 0 | +| `GetBackBuffer` | 1 | 1 / 0 | +| `CreateDepthStencilSurface` | 1 | 1 / 0 | +| `CreateOffscreenPlainSurface` | 1 | 1 / 0 | +| `GetDepthStencilSurface` | 1 | 1 / 0 | +| `CreateQuery` | 1 | 1 / 0 | diff --git a/docs/modern/D3D9_LIVE_CENSUS_frontend.json b/docs/modern/D3D9_LIVE_CENSUS_frontend.json new file mode 100644 index 0000000000..731f1a98df --- /dev/null +++ b/docs/modern/D3D9_LIVE_CENSUS_frontend.json @@ -0,0 +1,361 @@ +{ + "device": "0F8089A0", + "vtable": "0F80B8FC", + "budget": 3000, + "final": true, + "methods": { + "QueryInterface": { + "hits": 79, + "budget_exhausted": false, + "call_sites": { + "716ABF57": 79 + } + }, + "AddRef": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "7164267D": 2566, + "7164275B": 372, + "7165A3AC": 62 + } + }, + "Release": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "716426BD": 2566, + "71642794": 372, + "0355153D": 62 + } + }, + "TestCooperativeLevel": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "009C00CF": 2999, + "716C0A45": 1 + } + }, + "GetDeviceCaps": { + "hits": 2, + "budget_exhausted": false, + "call_sites": { + "00A0464A": 1, + "00A0A98F": 1 + } + }, + "SetCursorPosition": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "00AB5C8D": 3000 + } + }, + "Reset": { + "hits": 1, + "budget_exhausted": false, + "call_sites": { + "009BFCFF": 1 + } + }, + "Present": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "009BEED1": 3000 + } + }, + "GetBackBuffer": { + "hits": 1, + "budget_exhausted": false, + "call_sites": { + "009BFD90": 1 + } + }, + "CreateTexture": { + "hits": 282, + "budget_exhausted": false, + "call_sites": { + "009FA309": 282 + } + }, + "CreateVertexBuffer": { + "hits": 4, + "budget_exhausted": false, + "call_sites": { + "00A6318B": 4 + } + }, + "CreateIndexBuffer": { + "hits": 4, + "budget_exhausted": false, + "call_sites": { + "00AC1F9F": 4 + } + }, + "CreateDepthStencilSurface": { + "hits": 1, + "budget_exhausted": false, + "call_sites": { + "009F32FA": 1 + } + }, + "CreateOffscreenPlainSurface": { + "hits": 1, + "budget_exhausted": false, + "call_sites": { + "009F3288": 1 + } + }, + "GetDepthStencilSurface": { + "hits": 1, + "budget_exhausted": false, + "call_sites": { + "009BFDE7": 1 + } + }, + "BeginScene": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "009BEF2F": 3000 + } + }, + "EndScene": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "009BEF67": 3000 + } + }, + "Clear": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "009BE487": 3000 + } + }, + "SetTransform": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "00A0B12E": 912, + "00A0B3D4": 911, + "00A0B1D1": 911, + "7162D9E4": 256, + "7162D9AB": 1, + "7162D9C5": 1, + "7162DA07": 1, + "7162DA21": 1, + "7162DA3B": 1, + "7162DA55": 1, + "7162DA6F": 1, + "7162DA89": 1, + "7162DAA3": 1, + "7162DABD": 1 + } + }, + "SetViewport": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "71626520": 1500, + "7162666A": 1500 + } + }, + "GetViewport": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "716264EA": 3000 + } + }, + "SetRenderState": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "00A0593C": 2943, + "00A04551": 57 + } + }, + "SetTexture": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "00A0AEDE": 1262, + "009A0D27": 1258, + "009A2769": 184, + "009A33FD": 183, + "00B27E1B": 46, + "009A27A7": 46, + "7162DB37": 21 + } + }, + "SetTextureStageState": { + "hits": 240, + "budget_exhausted": false, + "call_sites": { + "00A0453E": 96, + "7162D1F9": 8, + "7162D211": 8, + "7162D229": 8, + "7162D249": 8, + "7162D261": 8, + "7162D279": 8, + "7162D291": 8, + "7162D2A9": 8, + "7162D2C1": 8, + "7162D2D9": 8, + "7162D2F0": 8, + "7162D308": 8, + "7162D320": 8, + "7162D338": 8, + "7162D350": 8, + "7162D368": 8, + "7162D380": 8, + "7162D398": 8 + } + }, + "SetSamplerState": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "00A05911": 2655, + "00A04526": 72, + "7162DB4F": 21, + "7162DB67": 21, + "7162DB7F": 21, + "7162DB97": 21, + "7162DBAF": 21, + "7162DBC7": 21, + "7162DBDF": 21, + "7162DBF7": 21, + "7162DC0F": 21, + "7162DC27": 21, + "7162DC3F": 21, + "7162DC57": 21, + "7162DC6F": 21 + } + }, + "SetScissorRect": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "71626571": 1500, + "71626684": 1500 + } + }, + "GetScissorRect": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "71626543": 3000 + } + }, + "DrawPrimitiveUP": { + "hits": 606, + "budget_exhausted": false, + "call_sites": { + "009DB64B": 606 + } + }, + "DrawIndexedPrimitiveUP": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "00A0AF2C": 2424, + "00A0AF76": 576 + } + }, + "CreateVertexDeclaration": { + "hits": 198, + "budget_exhausted": false, + "call_sites": { + "00A6306E": 198 + } + }, + "SetVertexDeclaration": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "009DA277": 1322, + "00BAE568": 715, + "009DB04F": 606, + "00BC74EB": 357 + } + }, + "CreateVertexShader": { + "hits": 198, + "budget_exhausted": false, + "call_sites": { + "00A6305B": 198 + } + }, + "SetVertexShader": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "009880AD": 3000 + } + }, + "SetVertexShaderConstantF": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "009897A9": 1309, + "00988E0A": 1249, + "00989AF3": 283, + "0098B952": 73, + "00989B47": 48, + "0098A789": 25, + "00989813": 13 + } + }, + "SetStreamSource": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "00A0B4BA": 1803, + "00B27DDC": 1197 + } + }, + "SetIndices": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "00B27DE9": 3000 + } + }, + "CreatePixelShader": { + "hits": 64, + "budget_exhausted": false, + "call_sites": { + "0098A9D8": 64 + } + }, + "SetPixelShader": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "00988A40": 2250, + "009881AE": 750 + } + }, + "SetPixelShaderConstantF": { + "hits": 3000, + "budget_exhausted": true, + "call_sites": { + "00989C9E": 3000 + } + }, + "CreateQuery": { + "hits": 1, + "budget_exhausted": false, + "call_sites": { + "009F755A": 1 + } + } + }, + "shared_bodies": {} +} \ No newline at end of file diff --git a/docs/modern/FRAME_TIMING.md b/docs/modern/FRAME_TIMING.md new file mode 100644 index 0000000000..1721a86a6c --- /dev/null +++ b/docs/modern/FRAME_TIMING.md @@ -0,0 +1,338 @@ +# Game-loop frame timing (retail Fable.exe) + +Static RE, 2026-10-16. Answers the MODERNIZATION.md open question "where the game loop reads frame +time, and whether a fixed-timestep split is reachable". Evidence is retail disassembly (capstone) +plus Ego_r.pdb member and method names. Function names marked † come from propagated +`coverage.tsv` labels; they sit inside the `CMainGameComponent` address cluster (0x415E00–0x418C00) +and match its PDB method list, but have not been run through `label_trust.py`. + +## Finding: the simulation is already fixed-timestep at 15 Hz + +- The world advances in **world frames ("server frames") at a constant 15 per second.** The rate is + the int32 global `0x1375550` = **15**. It is read at 632 sites in 461 functions and written by none + (static scan of every function). `GetConstantFPS` on `CObstacleNavigatorExternalInterface`, + `CGroundNavigatorExternalInterface` and `CGameScriptInterface` simply return it. Gameplay timers + multiply or divide by it (crime fines, drunkenness, AI deeds, save game). +- `CMainGameComponent` (PDB) is a lockstep server/client loop: `CurrentServerFrame`, + `InputServerFrame`, `ConvertSecondsToWF` / `ConvertWFToSeconds` / `ConvertGTToWF`, + `IsTimeForServerUpdate`, `GetRenderInterpolate`, `LastInterpolationInfo` (`CInterpolationInfo`), + `FrameRateSmoother`, `NoRenderFramesSinceLastGameUpdate`. +- **Rendering is decoupled and interpolated.** `IsTimeForServerUpdate`† (0x41674A) asks + `GetGameTimeOfNextPresentCompletion`† (0x4166E2: refresh-interval prediction from the display's + present statistics) and allows a world update only when that time has passed the next world-frame + boundary (`frame / 15`). Between world frames the renderer draws interpolated states. + Exactly: update iff `predictedPresentTime * 15 - frame > 1.0`. Two override paths force an update + on every call: `[0x13B8688] != 0`, or `[0x13B860C]` set with input-manager+0x38 set and + `[0x13B8629]` clear. All three bytes are `.bss` dev flags that no retail instruction writes (only + `IsTimeForServerUpdate`, `CMainGameComponent::Render` and `DoRender` read them). So statically, + retail's world tick cannot run faster than 15 Hz at any frame rate. +- Clock: `GFGetTime` (0x9E1BC0) returns seconds as a double from QueryPerformanceCounter, falling + back to `timeGetTime` when QPC is unavailable, relative to a stored base. It has 170 direct call + sites. `GetCurrentGameTime`† (0x416231) = `GFGetTime() - GameStartTime` (`this+0x60`). +- Main loop: `CMainGameComponent::Run` (0x4189C2) loops on either `UpdateForcedTickSpeed`† (0x417747, + console `ForceUpdateTick` mode with `ForceUpdateTickSpeedDesiredFramerate`) or `NewFrame` + (0x9F8BA0, a `timeGetTime` frame-rate counter) + `UpdateRegular`† (0x4162B5). `UpdateRegular` + times its two virtual calls (+0x14, +0x1C) with `GFGetTime` into the globals `0x13B8690` and + `0x13B8698` (per-phase durations). + +## Retail input recording already exists + +`CMainGameComponent` carries `EventPackageSet` (`CGameEventPackageSet`), `EventPackageFile`, +`LoadingEventPackages` / `SavingEventPackages`, `BeginInputSaving` / `BeginInputLoading`, +`WorldSeed` / `LocalSeed`, `CheckSync` and `PeekWorldChecksum`. Input arrives as per-world-frame +event packages, which is the lockstep design inherited from Xbox networked play. This is the +"input record/replay" tool that MODERNIZATION "Checking the fork" plans to build. The fork should +reuse it rather than invent one: record event packages on retail or parity, replay them on modern +x86/x64, and compare `PeekWorldChecksum` per world frame. + +### How it is switched on (and why retail can't) + +Static read, 2026-10-16: + +- `CMainGameComponent::Run` 0x4189C2 calls `BeginInputSaving` 0x416268 if byte `[0x13B85F6]` is set, + else `BeginInputLoading` 0x41627F if `[0x13B85F5]` is set. Both pass the `CWideString` global + `0x13B865C` as the file name. They forward to the input manager `[0x13B8388]`, to `StreamInputsToFile` 0x9F4CF0 and + `StreamInputsFromFile` 0x9F5E10 respectively, together with the object at `0x13B85E4`. + `coverage.tsv` labels both wrappers "BeginInputLoading"; the callees prove 0x416268 is Saving. +- `CMainGameComponent::InitWorld` 0x41735A calls `InitEventPackageLoading` 0x416056 if `[0x13B85F8]` + is set, and `InitEventPackageSaving` 0x4160CC if `[0x13B85F7]` is set. Both use the file name + `GEventPackageFileName` (`CWideString` at `0x13B8660`). The file is opened in the stream at + `this+0x16154`. Its header is one 4-byte version word (`this+0x16174`, written as 1). Per-frame + packages then go through `SaveEventPackage` 0x9F53C0 / `LoadEventPackage` 0x9F5650. +- **The four flags sit in `.bss` (zero at load), and no retail instruction writes them.** The only + references are the two readers above. They are not registered with `InitialiseConsoleVariables` or + `GFParseCommandLine`, which do handle the neighbouring dev globals in the same block. The file-name + globals are only constructed by their static initializers. So the recorder is compiled in but + unreachable. To use it on retail, a launcher or debugger must set the flag byte and the + `CWideString` before `Run`/`InitWorld`. That is the planned route for the fork's replay oracle. + `CWideString` is one pointer to a ref-counted `CWideStringData`. In retail that is **16 bytes**, + a `std::vector` {first, last, end} plus `NoRefs` at +0xC (allocator 0x99B3C0), not the + 28-byte `std::wstring` Ego_r.pdb shows. The readers take `operator const wchar_t*` (0x99AF00, + `[[s]]`) and copy the text, so the launcher allocates one block with a huge refcount. +- **Launcher: `tools/modern/recorder_poke.py --save-events `** (also `--load-events`, + `--save-inputs`, `--load-inputs`, `--status`, `--clear`), run while the game sits at the frontend. + Verified live 2026-10-16: starting a world runs `InitEventPackageSaving`, which creates the file + and writes the 4-byte version word `1`. The round trip is still untested. Every world load in the + shared install crashed before frame 1, recorder on or off (New Game and a Continue): + null physics mesh in `HasPhysicsMesh` 0xA23C40, reached from `OnCreate` 0x724010 ← + `LoadSingleThing` 0x51FD80 (FINDINGS_LOG 2026-10-16). It needs a launch that can load a world. +- Per world update (`CMainGameComponent::Update` 0x418289): when saving, after the world steps, + `AddEventPackageSetToSave` 0x4161A7 compresses the frame's `CGameEventPackageSet` (`this+0xA4`) + into the scratch region `0x13CB498` and writes `u32 size` plus `size` bytes. When loading, + `GetEventPackageSetFromSave` 0x416148 reads the same pair whenever `IsTimeForServerUpdate` + allows, decompresses it (0x9F1AC0) into `this+0xA4` and applies it (0x41726D) in place of + local input. File = `u32 version (1)`, then one record per world frame. +- Record encoding (`CGameEventPackageSet::CompressIntoBuffer` 0x9F19A0). In memory, the set is + {u32 count, packages[] stride 0x648}, a package is {u32 id @0, u8 eventCount @4, events[40] + @8 stride 0x28}, and an event is {u16 type @0, u8 @4, payload @5, u8 payloadLen @0x25, u8 flag + @0x27}. On disk: `u8 packageCount`, then per package `u8 eventCount, u32 id`, then per event + `u16 type | (flag ? 0x8000 : 0)`, `u8 [@4]`, `u8 len`, `len` payload bytes. +- Input stream shape (from `StreamInputsFromFile` / `LoadAllEventPackages` 0x9F5D70 / + `LoadEventPackage` 0x9F5650): a 12-byte header, then package records read until end of file into + a vector of 0x30-byte entries at `this+0x4C`. Each entry's +0xC is the input frame. Replay applies + entry `[this+0x58]` when its frame equals `this+0x5C`. `SaveEventPackage` 0x9F53C0 builds each + package from the input state (events are 0x34 bytes each) plus two floats from the device + `[this+0x2C]` (vcalls +0xC/+0x10, probably analog axes). Field-level layout is still to be read. + +## Consequences for the fork + +1. **Uncapped frame rate is reachable without a new timestep.** The world is already fixed at 15 Hz; + only render-side code must stop assuming the retail frame rate. The reported frame-rate bugs + (FABLE_TLC_REPORTED_BUGS: movement, multi-hit spells, physics/animation, menu repeat) therefore + point at code that reads *render* time (`GFGetTime`, `LastFrameRenderDuration`, `NewFrame`'s + counter, `GetRenderInterpolate`) and feeds it back into state. The 170 `GFGetTime` call sites and + the `LastFrameRenderDuration` / `FrameRateSmoother` readers are the audit list. +2. **Replay determinism is per world frame.** x87→SSE2 drift (MODERNIZATION "x64 hazards") is + measurable with the engine's own `PeekWorldChecksum` at each of the 15 Hz frames. +3. The world-frame constant must stay 15 in the fork. 461 functions are tuned in world frames. + +## GFGetTime callers classified so far + +| Function | Use | Frame-rate dependent? | +| --- | --- | --- | +| `UpdateFrame`† 0x4A5A40 (world) | wall-clock throttle: action once `GFGetTime() - [0x13B89F0] > 20.0` s; also stamps `this+0x180` | No (real seconds) | +| `CInputManager::Update` 0x9F57A0 | timestamps each input event with `GFGetTime()` before queueing (0xA03CB0 / 0xA03CE0) | No (real-time stamps) | +| `UpdateRegular`†, `Run`, `IsTimeForServerUpdate`†, `GetCurrentGameTime`† | loop scheduling and phase timing | No (scheduler) | +| `UpdateInterpolation`† 0x42DBFA | `(GFGetTime() - this+0xB8) * 15.0f` = render interpolation fraction in world frames | No (render only) | +| frontend `Update`† 0x42DC94 | `dt = GFGetTime() - this+0xC0`, **clamped to 0.1 s**, passed to the UI update 0x599E3F | Only below 10 fps (clamp slows UI); variable-dt above | +| frontend `Draw`† 0x42DF9E | stores the draw time (`this+0xBC`) | No | + +**2026-10-17 sweep of the render-side callers** (185 direct `GFGetTime` sites in 101 functions; +the instructions after each call read with capstone). No site feeds a render-rate count into state: + +| Pattern | Functions | Frame-rate dependent? | +|---|---|---| +| GUI container update: `dt = now - last; last = now;` then `child->vcall(+4)(dt)` for each child widget, run from `DrawPreparePrimitivesForRendering` | 0x47D1AC, 0x56F090, 0x581EBD, 0x59C7C7, 0x5C2FE1, 0x5E3EA0 (mislabelled `IsHeroControlledByPlayer`), 0x5FAF50, 0x61AD60, 0x625940, 0x7E49C0 | No by design: variable-dt widget animation at render rate. Each child's use of `dt` is not yet read. | +| Real-time threshold (`now - start` compared with a constant or a field) | `ChangeSelection` 0x494380 (selection repeat), `GlobalUpdate` 0x47E9AE, UI action delay 0x599E3F/0x59A238, speech/text timeout 0x716DE0, `ProcessTransitions`/`StartThemeTransition` (theme blend), `GFProcessBreath` (microthread budget), `ProcessObjects` 0x98ECD0 | No | +| dt-scaled accumulation (`acc += (now - last) * rate`) | `UpdateOffGameTickProcessInfo` 0xB23A90 | No | +| Profiling and fps statistics | world `Update` 0x49DFB0 (+0x90/+0x98 durations), `OnPostSwapScreens` (frame-time history), `FrameEnd` | No (display only) | +| Seconds converted to world frames (`× [0x1375550]`) | `SetUpTextAndSpeech` 0x716B30 | No | + +What is left of the audit is the child widgets' `vcall(+4)(dt)` bodies, and any per-render-frame +code that counts calls without reading the clock. Ego_r names give the complete render-rate hook +list to read: + +- GUI: the `NUISystem` components' `Update(float dt)` (28 classes: `CComponent`, `CList`, `CText`, + `CSlider`, `CScrollBar`, `CMovie`, `CMouseCursor`, `CMorphingSprite`, …) plus + `CComponent::UpdateZoom/UpdateColour/UpdatePosition(float)`. These are the container children above. +- World: `CWorld::RenderUpdate` → `CThingManager::RenderUpdateThings` → `CThing::RenderUpdate`, + and `CThingSoundEmitter::RenderUpdate`, `CPlayerGui::RenderUpdateGuiTimers` and + `CDisplayEngine::RenderUpdate`. All of these take `CInterpolationInfo` (a fraction between world + frames), not a count. `CTCBase::RenderUpdate(float)` exists, but no thing component overrides it + in Ego_r, so components do not get render-rate updates. +- Environment and detail: `CEnvironment::RenderUpdate(float)` and the `CLocalDetailCacheMap` + `RenderUpdate` family (foliage and detail LOD). + +**2026-10-17: render-rate hooks read (static; vtables recovered from RTTI, capstone).** + +- **Things.** `CThingManager::RenderUpdateThings` 0x51EBD0 calls vtable slots +0x54 and +0x68 + (the `RenderUpdate` pair, `CInterpolationInfo`) on every thing in each update list. In retail, + every CThing class's slot 26 is an empty `ret 4` (base 0x4CD1D0, creature 0x661A40, and player + 0x6A9550, which jumps to the creature stub). Slot 21 is `return false` (0x4C7460) for all but + `CThingSoundEmitter` (0x836AD0). That one moves the emitter's sound to the owner's + interpolated facing/position, and kills the emitter once its sound has stopped. So no thing, + creature or component changes state at render rate. +- **Environment.** `CEnvironment::RenderUpdate` 0x6B42F0 clamps the interpolation fraction to + [0, 1] and blends the previous and current theme values. Pure interpolation. +- **Camera.** `CGameCameraManager::GetRenderCamera(float)` 0x697F30 → + `GetInterpolatedCamera` 0x6978D0 lerps position, FOV and orientation between two stored + cameras (+0x04 and +0x3C). `CGameCameraManager::Update()` takes no dt. Camera motion is + world-tick, and the render only interpolates it. +- **GUI.** Slot 1 of every `NUISystem` class is `Update(float dt)`. `CComponent::Update` 0x531EC0 + adds dt to the component age (+0x30), then calls UpdateZoom 0x52F5C0, UpdateColour 0x52FFD0 + and UpdatePosition 0x52F900 with dt. Those evaluate curves of elapsed/duration. An automated + scan of all 50 slot-1 bodies (`dtscan`: member counters, constant float steps in bodies that + never read dt) found no per-call stepping. The overrides forward dt to their base and + otherwise act on one-shot pending flags (CScrollBar, CDraggable, CListArrow, + CSwappingStateComponent). CList's two constant adds are the compiler's unsigned-to-float + fix-up (2^32). +- **Loop bookkeeping.** `CMainGameComponent+0x16138` counts render frames (incremented by + `Render` 0x417001 and `ForceRender` 0x4164E0) and is reset per world frame, but nothing reads + it. The world period is 1 / (15 × forced tick speed at +0x38). The global 15 at 0x1375550 + converts seconds to world frames, and no render-rate input changes it. + +So the whole render path is interpolation, real-dt GUI curves, or real-time thresholds. Static +reading finds no code that advances game state per render frame. That leaves three ways a +high-fps report can be real: +(1) interpolation jitter, since the fraction comes from the predicted present-completion time +(`GetGameTimeOfNextPresentCompletion` 0x4166E2), which uncapped or VRR presentation can mispredict; +(2) a driver or vsync effect on the world tick's own scheduling, which only a live +`CurrentServerFrame`-vs-wall-clock sample settles; or +(3) the report is wrong. Further static reading is not the way to settle it. + +Before this audit, the timing code read so far was real-time or world-frame based, and none of +it explained a bug that appears only at *high* frame rates. The reported high-fps bugs must sit in code that counts render +frames (per-frame increments without dt) rather than code that reads the clock. That needs a +different scan: per-frame counters in render/UI update paths, starting under the UI update 0x599E3F. + +## Live results (2026-10-17, retail Fable.exe, validated Lua bundle, adult checkpoint save) + +Session scripts: `work/modern_live_20261017/` (not committed: `probe.py`, `inject.py`, +`early_patch.py`, `mem_patch.py`). All 563 save files were backed up, then restored and +hash-verified afterwards; the install was not modified. + +- **World tick: exactly 15.000 Hz.** The `CMainGameComponent` frame counter (+0x48) was polled + with ReadProcessMemory: 225 frames in 15.0 s, median step 66.67 ms, no multi-frame catch-up. + The render counter (+0x16138, reset every world frame) peaks at **4 renders per world frame** + in 224/225 frames, so render is 60.0 Hz on a 60 Hz mode with the game's own Vertical Sync + option on (defaults: `VerticalSyncEnabled TRUE;`, `RefreshRate 0;`). The game sets the + display mode itself (1024×768 at 60 Hz here, on a 165 Hz-capable monitor). +- **Uncapped / high-refresh not measured yet.** `RefreshRate 4` set in the profile does not + apply on load (the game rewrites the profile with `RefreshRate 0`). Video Options → Vertical + Sync OFF → Apply shows a 10-second real-time confirm (`GFGetTime` based, 0x47EA8D), which the + automated input could not accept: OK ignores clicks, ENTER and SPACE (FINDINGS_LOG + 2026-10-17). During the countdown the new mode is live, so the next try is an 8 s sample right + after Apply, or the present interval set in memory before device creation. +- **Mouse buffer overflow confirmed live, and the patch fixes it.** BPs right after both + `GetDeviceData` calls in the CMouseDX read loop (0xAB498C, 0xAB49B5) counted the HRESULT and + the records returned, while `inject.py` sent 1,500 single-count moves per `SendInput` call + (about 130-190 ms per burst, so 1,500 records reach the buffer within 2-3 world frames). + Retail (256): the burst reads back as a 255-record read flagged **`DI_BUFFEROVERFLOW`** + (2 in run 1, 24 in run 2). With `DIPROP_BUFFERSIZE` 8,192 written into memory 0.3 ms after + the process appeared (`early_patch.py`, before the mouse device exists), the same burst drains + as 256 + 256 + ... + remainder reads, all `DI_OK`, **0 overflows**. The read loop refills as + predicted. A patch written later does not take effect: the device is created at startup and + is not recreated on world entry. +- Debugger gotcha: detaching dbgeng from a running target after a `go()` timeout failed + ("target not accessible") and left every game thread suspended. Resuming them killed the + game, because the dead session's break-in thread was parked on `int3`. End a session from a + breakpoint handler that returns BREAK, then clear the BPs and detach. + +### "Per frame" tunables (PDB member names) + +Of the PDB members named per-frame, most sit on thing components (`CTCDivineWrath::NumFramesBetweenDamageHit`, +`CTCTurncoatSpell::PerFrameStaminaModify`, `CTCForcePushed::DistanceMovingPerFrame`, ...). Those +advance on the world tick and are frame-rate-safe, because the world cannot run faster than 15 Hz +(static result above). So if the "multi-hit spell damage changes with frame rate" report is real, +the cause is not the world tick. A live sample of `CMainGameComponent::CurrentServerFrame` against +wall time at uncapped vs vsynced rates would confirm this. + +**Cleared: `CPlayerMovementDef::LeanAngleStepPerFrame` is per world frame.** Retail chain: +`CThingPlayerCreature::WorldUpdate` (vtable 0x12457FC slot 19) → `UpdatePlayerControl` 0x6AD970 → +`ControlledMove` 0x6AD700 → 0x6AD260 → tail jump to `ResolveFacingDirection` 0x6AB820 → +`GFSmoothTowards` 0x6ADC20, which moves `LeanAngle` toward its target by at most +StepPerFrame. Retail offsets: def pointer `this+0x1EC`, `LeanAngle` `this+0x1FC` (Ego_r +0x218 / ++0x228). The def fields are Ego_r −4: TurnFactor +0x70, LimitAngle +0x74, StepPerFrame +0x78, +each scaled by 1/360 (degrees to turns). The lean is zeroed when the hero's position delta is +below a threshold, and clamped to ±LimitAngle. So the confirmed "camera wobble" bug is about +how the camera uses the lean, not frame rate. + +Stock values (`MOVEMENT_DEFAULT`, `PLAYER_MOVEMENT` entry 7076 of `game.bin`; field keys are +CRC32 seed 0 of the member name): TurnFactor 60, LimitAngle 18°, StepPerFrame 3.6° per world +frame. That is full lean in 5 world frames (1/3 s). The Nexus mod "Camera Wobble Remover" +(`FableTLC-CameraWobbleRemover.fmp`, a FableMod patch of that one entry) sets exactly these three +to 0 and leaves the other 13 fields stock. That confirms the wobble is the lean. In Ego_r, only +`CThingPlayerCreature`'s movement and facing code reads `LeanAngle` (`ResolveFacingDirection`, +`Resolve/ApplyRelativeMovementAcceleration`, `OnCreate`), and no camera or render function does. +The lean bends the hero's facing, and the follow camera inherits it. For the fork this is a +settings toggle (scale or zero the three def fields), not a timing fix. + +**Cleared (static, 2026-10-17): in-game mouse look is stepped per world frame.** + +- `CMouseDX::Update` 0xAB58E0 (vtable 0x129EA14 slot +0x14) drains the DirectInput buffer + (`GetDeviceData`, 0x14-byte records, up to 256 per read, re-`Acquire` on `DIERR_INPUTLOST`), sums + the X/Y/Z deltas, and with filtering on (`+0x484B`) averages them with the previous call's deltas + (×0.5 constant 0x122F59C). It queues **one** input event per call, and when re-centring + (`+0x343E`) is on it calls `SetCursorPos` to the client centre. The deltas are DirectInput relative + data, so re-centring loses no motion. +- `CInputManager::Update` 0x9F57A0 (vtable 0x129C79C slot +8) calls the mouse (`[this+0x24]`, +0x14), + the keyboard (`[this+0x28]`, +4) and each pad in `[this+0x2C..0x30]` (+0x28). +- In-game, its only driver is `CMainGameComponent::GetInputs` 0x416E78 (vtable 0x122F180 slot +0x18, + calls `[0x13B8388]` +8). `CMainGameComponent::Update` 0x418289 calls `GetInputs` only inside the + `GetGameEventPackageSet` 0x4AEBA0 branch, which is gated by `IsTimeForServerUpdate`. So the device + is read **once per world frame (15 Hz)**, and each event carries all motion since the previous world + frame. +- `CInputProcessCameraLookAround::ProcessInput` 0x68D790 reads analog action 0x4D or 0x3D (or digital + 0x3E/0x3F as ±1) and **sets** the look vector with 0x697B10 (`+0xCC/+0xD0` = value × 0x125C1E4/E8, + then the camera's +0x90 via 0x84CF40). That is last-wins, which would lose motion if events + arrived faster than they are consumed. With one event per world frame it doesn't. +- The per-render-frame `CMouseDX::Update` calls in the live trace were taken at the frontend, which + is not in this path. The second "reference" at 0x129D628 is slot 2 of `CInputManagerDX`'s own + vtable (0x129D620, from RTTI), and it is the frontend's driver: `Input` 0x42E3EE calls + `[0x13B8388]->vtbl[2]` (Update) at 0x42E434, then `EventScanStart`. The frontend is driven by + `CNewFrontendGameComponent::Run` 0x42EC7C, which calls `Input` 0x42E3EE (→ `[0x13B8388]` +8), + `Update` 0x42DC94, `UpdateInterpolation` and `Draw` once per **render** frame, so menu input is + render-rate. Its key and stick repeat uses the real-time `CStopWatch` at `+0xD8` (0.333 s initial + delay `0x3EAA7EFA`, then 0.085 s `0x3DAE147B`), so it is not frame-rate dependent. + (`CFrontendGameComponent::Run` 0x496CF0 → `Input` 0x4963A0 is the other frontend component; its repeat is not yet read.) +- Consequence for the fork: raising the render rate leaves mouse sensitivity unchanged, as long as the + input service keeps sampling at the world-frame boundary. A per-render-frame camera (smoother + mouse look) would need the look vector to accumulate instead of being set: a switch, not the default. +- **Buffer size: 256 records** (`CMouseDX::SetExclusiveMode` 0xAB5710 creates the device, then + `SetCooperativeLevel` 5 = exclusive|foreground or 0xA = non-exclusive|background, + `SetProperty(DIPROP_BUFFERSIZE, 0x100)`, `SetDataFormat(c_dfDIMouse2 0x12ADDFC)` falling back to + `c_dfDIMouse` 0x12ADBF4, then `Acquire`). The buffer is shared by axes and buttons and is drained + once per world frame (66.7 ms). A 1000 Hz mouse moving diagonally fills about 133 records per frame + and fits. A 4000/8000 Hz mouse fills about 530/1,070 and overflows (`DI_BUFFEROVERFLOW`), so mouse + look loses motion on modern high-polling mice, and on any hitch longer than about 128 ms at 1000 Hz. + Fork: the input service accumulates raw-input deltas with no record cap and hands the world one + summed delta per world frame. That keeps retail behaviour and removes the overflow. Retail + workaround: set the mouse to 1000 Hz or lower. +- **Candidate retail patch (not applied, untested in-game).** The read helper 0xAB4910 refills its + 256-record cache with `GetDeviceData` whenever the cache is exhausted, and `CMouseDX::Update` + calls it until it returns nothing. So the per-call 256 is not a cap, and only the device buffer + size limits a frame. Raising both `DIPROP_BUFFERSIZE` immediates in `SetExclusiveMode` is enough: + the dword at VA 0xAB57BA (file 0x6B57BA, exclusive branch) and VA 0xAB5871 (file 0x6B5871, + non-exclusive branch), each `00 01 00 00` (256) in Steam `Fable.exe` (SHA-256 prefix + `41dc91090ae85371`, 16,666,624 bytes), changed to e.g. `00 20 00 00` (8,192, enough for an 8 kHz + mouse at 15 Hz with headroom for hitches). + Tool: `python tools/modern/mouse_buffer_patch.py --exe --out [--records N]`. + It accepts only the unmodified Steam executable (full SHA-256), checks both `C7 44 24 24 00 01 + 00 00` sites, writes a copy (it never modifies the input), and changes exactly 2 bytes. `--check` + reports a file's current buffer size. The same value written into memory was verified live + ("Live results" below). + +**Cleared: the GUI, sprite and speech-gain per-frame tunables** (Ego_r readers by member offset, +then the retail counterpart by call order): + +- `NumberOfCharactersScreenMessageAddsPerFrame` (screen-message typing) is read only by + `CPlayerGui::Update(bool)` / `CScreenMessage::Update`. `CPlayerGui::Update` (retail 0x43A080 on + singleton `[0x13B8790]`) is called only from `CWorld::UpdateFrame` (retail 0x4A5A40, call + 0x4A5E4D; same call sequence as Ego_r 0x4AF3D0). In Ego_r, `CWorld::UpdateFrame`'s only caller + is `CWorld::EAUpdateFrame(const CGameEvent*)`: the world frame is itself a game event, so this + runs per world frame. +- `SmackAlphaFadePerFrame`, `SmackScaleFactorPerFrame` and the other `Smack*` fields: in Ego_r + nothing besides `CPlayerGuiDef::Transfer` and `operator=` touches them, so they are loaded but unused. +- `CTCGraphicAppearanceSprite::RotationAmountPerFrame`: `FrameUpdate` copies `RotationAngle` to + `OldRotationAngle` and `AnimStage` to `LastAnimStage`, then advances them, and the draw + interpolates (`GetInterpolatedAnimStage`). That is the engine's world-step + render-interpolate + pattern. +- `CResponseTimes::Attack/ReleaseMaxDeltaPerFrame` belong to `NSpeechGainManager` (speech + ducking), not input. `NSpeechGainManager::Update` (Ego_r 0x70E0F0) computes + `GetCurrentServerFrame() - lastFrame` and steps each gain by that many world frames, so its + call rate does not matter. + +No named per-frame tunable has turned out to be render-frame dependent. The remaining candidates +are code that counts render frames without a name saying so (the frontend `Update` variable dt; +`CMouseDX::Update` is cleared above). + +About 40 of the rest are loaders, decompressors and profiling (`LoadFromFile`, `Decompress`, +`PreloadCompletedBank`, `LoadNewScene`, save/load), which are frame-rate neutral by construction. + +## Next + +- ~~Classify the remaining `GFGetTime` callers~~ done 2026-10-17 (table above). Next is the GUI + child widgets' `vcall(+4)(dt)` bodies. +- Event-package record/replay on retail: the launcher and record format are done (above). Still + needed is a launch that loads a world (the shared install's plain launch crashes in region thing + load). Then record a short session, relaunch with `--load-events`, and compare world state. diff --git a/docs/modern/MODERNIZATION.md b/docs/modern/MODERNIZATION.md new file mode 100644 index 0000000000..bdbfc28df2 --- /dev/null +++ b/docs/modern/MODERNIZATION.md @@ -0,0 +1,291 @@ +# Modernization fork — x64, D3D12 + Vulkan, ffmpeg, moddability + +Plan, 2026-09-26. Status: **design agreed, nothing built**. Owner decisions (user, 2026-09-26) are +marked **[decided]**. Evidence for the platform list: [PLATFORM_SURFACE](PLATFORM_SURFACE.md) +(generated). Scripting runtime: `docs/engine/SCRIPTING_REDESIGN.md` (lands with the docs reorganization). Layer rules: +[ARCHITECTURE](../ARCHITECTURE.md). + +Contents + +- [Goal](#goal) +- [Decisions](#decisions) +- [Reconstruction standard: functional first, byte-close](#reconstruction-standard-functional-first-byte-close) +- [The fork gate](#the-fork-gate) +- [Steps](#steps) +- [x64 hazards](#x64-hazards) +- [Graphics](#graphics) +- [Audio, video, input, platform](#audio-video-input-platform) +- [Player-facing features](#player-facing-features) +- [Linux without a Linux machine](#linux-without-a-linux-machine) +- [Mods across the fork](#mods-across-the-fork) +- [Checking the fork](#checking-the-fork) +- [Work that can start now](#work-that-can-start-now) +- [Open questions](#open-questions) + +--- + +## Goal + +Once the reconstruction plays TLC through, turn it into our own program: 64-bit, modern graphics +(Direct3D 12 and Vulkan), ffmpeg for video, first-class modding. It still loads the player's retail +data and retail saves. + +**Not goals:** shipping retail assets; changing gameplay by default (fixes go behind switches); a +rewrite from scratch. The fork starts from the reconstructed source and changes it step by step. + +## Decisions + +| Question | Answer | +|---|---| +| Byte purity | **[decided]** Functionally identical is the requirement; byte parity within a reasonable margin (see next section). `__asm`/naked bakes still do not count. | +| Platforms | **[decided]** Windows first. Linux is wanted, kept buildable, not yet tested (no Linux machine or VM). | +| Graphics | **[decided]** Hand-written D3D12 and Vulkan backends behind our own render interface (RHI). No third-party abstraction layer. | +| Location | **[decided]** Separate CMake tree in this repo (`modern/`), apart from the VC7.1 parity tree. | +| Fork gate | Source-complete, not byte-complete (below). | + +- **Compression: open source where we can** (decided 2026-10-17, user: "Open source where we + can"). Reading retail data needs LZO1X and zlib, and the fork reads LZO1X with its own MIT + decoder (`rebuild/modern/codec`). Files the original game must still load are written as LZO1X + with the MIT encoder ported from FableForge, or in retail's stored form. The fork's own new + formats (new saves, caches, mod packages) use zstd (BSD) by default, LZ4 (BSD) where + decode speed matters more than size, and zlib where retail compatibility asks for it. Upstream + LZO (GPL) stays an optional, never-vendored test dependency for byte-identical retail rewrites + (`FABLE_SAVE_WITH_LZO`), and nothing the fork ships needs it. + +## Reconstruction standard: functional first, byte-close + +This replaces "every function must be a byte-exact MATCH" as the bar for counting a function as +reconstructed. It applies to the parity tree, not just the fork. + +A function is **reconstructed** when all of these hold: + +1. **Genuine C++** written from the decompilation. `__declspec(naked)`, `__asm`, and `_emit` remain + `asm_bake`: an oracle, never reconstruction. +2. **Functionally identical**: a behaviour fixture passes against the retail oracle (same outputs, + same side effects on the modelled state, same calls in the same order). +3. **Byte-close** under VC7.1: the new `near_match` grade, proposed defaults (tune after the first + sweep): + - instruction count within ±10% of retail, and + - ≥ 90% opcode-sequence similarity after masking register numbers, relocations, and stack + offsets. + +`MATCH` / `RELOCATION_MATCH` stay the best grades and are still worth taking when a spelling gets +there. What changes is how far to chase: once a function is `near_match` and its fixture passes, stop +unless the remaining diff hides a behaviour difference. Register-allocation and scheduling dead ends +(the documented VC7.1 cases) are `near_match`, not open work. + +What stays forbidden: invented algorithms, fitted constants, and hand-made tables in place of the +engine's own code (see the data-writer rule in the project memory). "Functionally the same" means +the same algorithm, compiled. + +Needed tooling: a similarity score in `verify_and_land.py` (today it records only +`DIFFER(built v retail)` lengths) and a `near_match` column in the coverage dashboard. + +## The fork gate + +The parity build links reconstructed functions next to retail bytes. A 64-bit build cannot contain +any retail x86 bytes, so the fork needs **C++ for every function it links**. With the standard above, +`near_match` and `functional` source both qualify. The gate is: + +- **Source-complete** for the code the game runs (boot → frontend → world → every quest), and +- the reconstructed x86 build **plays through** on the autopilot/runner (the existing + in-game harness), and +- retail saves load in it. + +The VC7.1 parity tree does not stop at the fork. It remains the oracle the fork is compared against. + +## Steps + +Each step changes one thing, keeps the game playable, and is compared against the step before it. + +| # | Step | Changes | Exit test | +|---|---|---|---| +| 0 | Seams (now, in the parity tree) | Platform calls sit behind named services; no D3D9/DInput/OpenAL/DirectShow types in gameplay headers | Header rule holds; PLATFORM_SURFACE functions all tagged with their service | +| 1 | Modern compiler, still x86 | CMake tree, current MSVC and clang-cl, `/W4`; retail CRT use becomes standard library | Replay (below) matches the parity build | +| 2 | x64, still D3D9 | Pointer-size fixes, explicit serializers (hazards below). 64-bit `d3d9.dll`, OpenAL Soft, DirectShow still present | Replay matches; retail saves load; a save round-trips | +| 3 | Platform layer | Window, input, timing, files, config, paths, audio device, video decode on our own interfaces; ffmpeg replaces DirectShow; OpenAL Soft replaces `wrap_oal` | Replay matches; no Win32 calls outside `platform/win32/` | +| 4 | RHI + new backends | D3D9 calls behind the RHI; D3D9 backend first (identical frames), then D3D12, then Vulkan | Frame captures match the D3D9 backend within tolerance | +| 4b | Settings + controllers | Settings screen and config file; controller support, graphics options, gameplay/accessibility switches ([Player-facing features](#player-facing-features)) | Every setting round-trips through the config; defaults reproduce retail | +| 5 | Moddability | SCRIPTING_REDESIGN steps 4–7, mod loader, FableForge as the editor | Converted quests and FSE-shim mods pass their runs | + +Compatibility fixes (the AMD fade/audio reports, reported-bug list) land behind switches, default +off, after step 2. + +### Status (2026-10-17) + +The fork gate (a runnable reconstruction) is not open yet. Each step's groundwork is measured +against the reconstructed code that exists: + +| # | Where it stands | Evidence | +|---|---|---| +| 1 | Reconstructed code survives VS 2022 x86: 99.4% of 20,327 TUs compile; 315/326 behaviour tests that pass under VC7.1 still pass (with `compat::retail_bitset`); the parity lane's reconstructed title raster draws byte-identical frames | [MODERN_COMPILE_CENSUS](MODERN_COMPILE_CENSUS.md), [MODERN_BEHAVIOR_CENSUS](MODERN_BEHAVIOR_CENSUS.md), `tools/modern/modern_title_raster.py` | +| 2 | 96.5% of TUs compile clean as x64 after two byte-neutral header fixes; the reconstructed title raster runs as x64 with byte-identical frames; every x64 behaviour failure traces to a 32-bit test harness; retail saves (402/402) and `.big` banks (17/17) round-trip through portable codecs | census reports above, `rebuild/modern/save`, `rebuild/modern/bank` | +| 3 | Started: Raw Input mouse accumulation (no 256-record limit, verified against injected 8 kHz-class bursts) and the world-tick clock (retail's `IsTimeForServerUpdate` semantics) | `rebuild/modern/platform` | +| 4 | Pixel path proven: retail textures decode and present through D3D12 and Vulkan with pixel-identical captures; no RHI yet | `rebuild/modern/texture` (`fable_title_view`) | +| 4b, 5 | Not started in the fork tree | - | + +## x64 hazards + +The API calls are the easy part. These are where 64-bit breaks things quietly: + +- **Persisted layouts.** Any struct read or written as raw bytes with a pointer, `long`, or + pointer-sized member changes size. Candidates: saves (`CPersistContext`), game.bin defs + (`Transfer`), bank/`.big` headers, `.lev`/`.tng`/`.stb` readers. Each gets an explicit + serializer that reads the retail 32-bit layout. Retail saves must load; our saves stay + retail-readable until a documented format bump. +- **PDB layouts are 32-bit.** `rebuild/include/engine/*.h` and `fable_types.h` are parity-only. The + fork needs its own types; offset asserts become 32-bit-only or move to the serializers. +- **Pointers in integers.** `DWORD` handles, `SetWindowLong(GWL_USERDATA)`, pointer→int hashing and + sorting, `int` sizes. `/W4` + `-Wshorten-64-to-32` in step 1 find most of them. +- **x87 → SSE2.** Float results change slightly. Physics, animation, navmesh, and AI thresholds may + drift, so replay comparison needs tolerances and a divergence report, not exact equality. +- **Struct packing and alignment** change with pointer size; anything shared with data files must be + explicit. +- **Retail COM, CRT, and STLport-era idioms** (VC7.1 `std::` layouts, `_Uninit_copy` families) + become the modern standard library. + +## Graphics + +**[decided]** Hand-written RHI with D3D12 and Vulkan backends, so we control it. + +- The RHI is shaped by what Fable actually does, measured from the reconstruction: the + `IDirect3DDevice9` call-site census (open task), vertex/pixel shader set (`VSHADER_*` names in the + exe), render targets, fixed-function states in use. +- A D3D9 backend comes first and must reproduce retail frames. It is the reference the new backends + are measured against, and it lets the RHI land without changing the picture. +- Shaders: retail D3D9 shader programs (assembly or compiled bytecode) get re-authored in HLSL, + compiled with DXC to DXIL (D3D12) and SPIR-V (Vulkan). Fixed-function paths become shaders. +- D3D12 before Vulkan on Windows (debug layer and PIX); Vulkan next, since it is the Linux path. + RenderDoc covers both. +- D3DX9 (`d3dx9_25.dll`, 11 imports) is replaced by our own code: matrix math, texture loading + (the DXT/surface loader is engine code anyway), shader compilation at build time. + +## Audio, video, input, platform + +From [PLATFORM_SURFACE](PLATFORM_SURFACE.md): + +- **Audio is OpenAL**, loaded at runtime (`OpenAL32.dll` / Creative `wrap_oal.dll`, DirectSound3D + underneath). Not Miles. OpenAL Soft keeps the same API and already runs on x64 and Linux, so audio + is mostly a library swap. Xbox ADPCM decode stays engine code (ffmpeg decodes `adpcm_ima_xbox` as + an oracle; `tools/xbadpcm.py` is byte-exact against it). +- **Video is DirectShow** via COM (`CVideoSys`, see [VIDEO_SYSTEM_RE](../engine/VIDEO_SYSTEM_RE.md)). + ffmpeg decodes the retail `.wmv` (WMV + WMA) into the same texture handoff `DoRenderSample` + performs today. Link ffmpeg as LGPL shared libraries. +- **Input is DirectInput 8** plus IME (`imm32`). The platform input service does raw input and + XInput/GameInput on Windows, evdev later. This is where the shelved gamepad-redefine work + ([GAMEPAD_REDEFINE_PATCH](../engine/GAMEPAD_REDEFINE_PATCH.md)) gets revisited. +- **OS**: `kernel32` (518 calling functions, mostly critical sections, files, threads), + `advapi32` registry → a config file, `winmm` timing → the platform clock, `wsock32` only for cut + co-op, `ConfigDetect.dll` hardware tiers → our own probe on the RHI device. +- Platform code lives in `modern/platform/win32/` (later `modern/platform/posix/`); nothing else + includes ``. + +## Player-facing features + +What players actually notice. All of it lives in the fork. Retail defaults stay the defaults. +Everything is stored in one config file (step 3), editable from an in-game settings screen built +on the engine's own frontend UI, so it looks like Fable, not like a launcher. + +**Controller support** (lands with the step 3 input service): + +- Full gamepad play through XInput / GameInput on Windows and evdev on Linux. Hot-plug, several pad + types, button prompts matching the pad. +- Rebinding for keyboard, mouse, and pad on the full action list. The retail action enums are + proven from FableWin.pdb ([CONTROLLER_ENUMS](../engine/CONTROLLER_ENUMS.md)); the Xbox disc has the + original pad bindings as evidence ([XBOX_DISC_ASSESSMENT](../engine/XBOX_DISC_ASSESSMENT.md)). The + shelved retail patch ([GAMEPAD_REDEFINE_PATCH](../engine/GAMEPAD_REDEFINE_PATCH.md)) and the + missing `EGameAction` name table stop mattering once the input layer is ours. +- Stick dead zones, sensitivity, invert axes, right-stick camera (`AllowRightStickRotation` / + `AllowRightStickZoom` already exist as engine settings). + +**Graphics settings** (land with step 4, the RHI): + +- Any resolution, borderless / exclusive fullscreen / windowed, monitor selection, vsync, + frame-rate cap. +- Widescreen done properly: horizontal-plus FOV, UI laid out for the aspect ratio, mouse mapping + that follows the resolution (a reported bug: resolution changes break UI scaling and mouse + mapping). +- Quality: texture filtering (anisotropic), MSAA or a post AA, shadow resolution, draw and LOD + distance, foliage density, render scale. Retail quality tiers from `ConfigDetect.dll` become + presets. +- Later: HDR output, ultrawide HUD placement. + +**Frame rate.** [FABLE_TLC_REPORTED_BUGS](../engine/FABLE_TLC_REPORTED_BUGS.md) lists movement, +multi-hit spell damage, physics/animation, and menu repeat as changing with frame rate. Before the +cap can be raised, the simulation has to run at a fixed timestep with rendering interpolated between +steps. That needs the game loop's timing RE first (the step-2 replay will show where the logic reads +frame time). Until then, the cap stays at the retail rate. + +**Gameplay and accessibility settings** (switches, after step 2, default = retail): + +- Subtitles on/off, text size, speaker names; HUD elements on/off; camera distance and FOV. +- Separate audio volumes (music, effects, voice, ambient) and output device selection. +- The reported-bug fixes, each as its own switch, grouped as "retail behaviour" / "fixed". +- Optional quality-of-life changes (e.g. difficulty or combat tuning) only as clearly labelled + switches, so a default run is still TLC. + +## Linux without a Linux machine + +- **CI builds Linux** on a GitHub Actions `ubuntu` runner from step 1 on: compile, unit tests, + headless tests (script host, serializers, replay without rendering). Breakage shows up per commit. +- **WSL2** on this Windows 11 machine can run the same headless build and tests locally without a VM. +- Real Linux play-testing (Vulkan on a GPU, audio, input) waits for a Linux install; until then the + docs call Linux "builds, untested". + +## Mods across the fork + +| Mod kind | Carries over | Notes | +|---|---|---| +| Data: `.big`, defs, `.lev`/`.tng`/`.wad`, text, `.qst` | Yes | Same retail formats | +| FableForge output (levels, NPCs, quest cards, mod packs) | Yes | FableForge writes retail formats | +| Converted quest Lua / ForgeFSE Lua mods | Yes, via the FSE shim | SCRIPTING_REDESIGN "Compatibility" | +| Native hooks (ForgeFSE detour at `0xCDB355`, EgoCore DLL mods, byte patches) | **No** | They patch x86 addresses that do not exist in the fork; their features become engine code or script API | + +## Checking the fork + +- **Input record/replay** is the fork's first tool: record inputs and RNG seeds on one build, replay + on another, compare the world state stream (hero position, quest states, script events) and + sampled frames. Runs across retail, parity, modern-x86, and x64 builds. +- The existing autopilot / in-game runner, visual-parity screenshot tools, and save checkpoints + (`adult_*_completed_*`) are the test corpus. +- Save compatibility: every checkpoint loads in the fork; fork saves load in retail until a format + bump. + +## Work that can start now + +These help the current work and cost nothing later. They are tracked in +[ROADMAP](../ROADMAP.md) section (h). + +- [x] Platform surface inventory: `tools/modern/platform_surface.py` → + [PLATFORM_SURFACE](PLATFORM_SURFACE.md). +- [ ] `near_match` grade: similarity score in `verify_and_land.py`, dashboard column, rerun + over existing `DIFFER` rows (many should reclassify). +- [ ] Header rule in the parity tree: no D3D9/DInput/OpenAL/DirectShow types in gameplay-facing + headers; add a verifier warning. +- [x] Persisted types worklist: [PERSISTED_TYPES](PERSISTED_TYPES.md) (2026-10-16). +- [~] `IDirect3DDevice9` call-site census: static lower bound in [D3D9_DEVICE_CENSUS](D3D9_DEVICE_CENSUS.md). +- [x] Modern-compiler census (steps 1-2 readiness): every reconstructed TU through VS 2022 x86 and x64, + [MODERN_COMPILE_CENSUS](MODERN_COMPILE_CENSUS.md) (2026-10-17). +- [ ] SCRIPTING_REDESIGN "Now" steps 1–3 on ForgeFSE. + +## Open questions + +- **Reuse FableForge `forgecore` for level/world formats?** (Recommendation 2026-10-17: yes.) + `D:\Code\FableForge\libs\forgecore` is MIT C++ by the same owner. It already reads and + writes `.lev`, `.tng`, `.wld`, `.wad`, `.stb` (with bake/validate), `.bwd`, `.qst`, game.bin + defs, `.lut` and lipsync, and its LZO follows the same licence rule as `rebuild/modern/save` + (clean-room MIT LZO1X, `lzo1x_999` only via an optional system liblzo2). Re-deriving those + serializers here would duplicate months of validated work. The fork's own modules cover what + forgecore does not: `save/` is byte-exact read+write of every body section (forgecore's save + reader is read-only: HEADER plus hero stats), and `bank/` is a layout-preserving `.big` round + trip with payload views. Proposed split: the fork links forgecore for level/world/def formats, + and forgecore can adopt `save/` and `bank/`. The open part is how the fork tree pulls it in (git + submodule, FetchContent of a tagged release, or copying a pinned snapshot). +- Thresholds for `near_match` (the ±10% / 90% defaults) after the first sweep over existing + `DIFFER` rows. +- Whether to record the replay stream from retail itself (a small ForgeFSE-side recorder) or only + from the parity build. +- Vulkan minimum version and D3D12 feature level (proposal: Vulkan 1.3, D3D12 FL 11_0 + DXIL SM 6.0). +- Save format bump policy once the fork adds state retail cannot read. +- ~~Where the game loop reads frame time~~ answered in [FRAME_TIMING](FRAME_TIMING.md): the world is + already a fixed 15 Hz step with render interpolation; the remaining work is auditing render-time readers. diff --git a/docs/modern/MODERN_BEHAVIOR_CENSUS.md b/docs/modern/MODERN_BEHAVIOR_CENSUS.md new file mode 100644 index 0000000000..2b00b6bb2f --- /dev/null +++ b/docs/modern/MODERN_BEHAVIOR_CENSUS.md @@ -0,0 +1,27 @@ +# Modern behaviour census (fork steps 1-2, behaviour) + +Generated by `tools/modern/modern_behavior_census.py`; do not hand-edit. Each behaviour test that +`#include`s the reconstructed source it checks is built and run with the parity compiler (VC7.1), +VS 2022 x86, and VS 2022 x64 (census header copy: pointer-size check and PDB size asserts off). +A pass is exit code 0. [MODERN_BEHAVIOR_CENSUS.tsv](MODERN_BEHAVIOR_CENSUS.tsv) lists every test whose +outcome differs from VC7.1. + +Modern configs' `CThing::UsingComponents`: `fable::compat::retail_bitset<274>` (VC7.1's 36-byte layout; `--retail-bitset`). + +Tests: **376** + +| Outcome | VC7.1 | VS 2022 x86 | VS 2022 x64 | +|---|---:|---:|---:| +| pass | 325 | 325 | 178 | +| fail | 10 | 1 | 69 | +| crash | 0 | 1 | 35 | +| timeout | 1 | 0 | 1 | +| compile_fail | 28 | 37 | 78 | +| link_fail | 12 | 12 | 15 | + +Of the 325 tests that pass under VC7.1: **314** still pass under VS 2022 x86 and **177** under VS 2022 x64. + +x64 runtime regressions: **105**; **105** have a 32-bit shortcut in their test harness (97 fixed 32-bit storage array, 29 cast of base + constant offset, 24 hard-coded byte offset), i.e. the test fixture, not the reconstructed code, assumes 32-bit layouts. + +VC7.1-passing tests that do not pass on x86: 8 compile_fail, 1 fail, 1 crash, 1 link_fail. +VC7.1-passing tests that do not pass on x64: 69 fail, 39 compile_fail, 35 crash, 4 link_fail, 1 timeout. diff --git a/docs/modern/MODERN_BEHAVIOR_CENSUS.tsv b/docs/modern/MODERN_BEHAVIOR_CENSUS.tsv new file mode 100644 index 0000000000..54cdaa50a1 --- /dev/null +++ b/docs/modern/MODERN_BEHAVIOR_CENSUS.tsv @@ -0,0 +1,161 @@ +test vc71 modern86 modern64 detail +00/40/CVectorMapThingComponents_LowerBound_0040f020_test.cpp pass pass fail +00/41/CCountedFontPointer_Sharing_004190e2_test.cpp pass pass crash exception 0xC0000374 +00/41/CDisplayEngineInit_CDisplayEngineInit_00419270_test.cpp pass pass fail +00/41/CMainGameComponentInit_CMainGameComponentInit_004131a0_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\41\../../../src/compiled/00/41/CMainGameComponentInit_CMainGameComponentInit_004131a0.cpp(13): error C2485: 'naked': unrecognized extended attribute +00/41/CMainGameComponentInit_CMainGameComponentInit_00415e17_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\41\../../../src/compiled/00/41/CMainGameComponentInit_CMainGameComponentInit_00415e17.cpp(19): error C2485: 'naked': unrecognized extended attribute +00/41/CMainGameComponent_InitEventPackageLoading_00416056_test.cpp pass pass crash exception 0xC0000005 +00/41/CMainGameComponent_InitEventPackageSaving_004160cc_test.cpp pass pass crash exception 0xC0000005 +00/41/CMainGameComponent_PostChangeResolution_00416365_test.cpp pass pass crash exception 0xC0000005 +00/41/CMainGameComponent_ProcessEventPackage_00416670_test.cpp pass pass crash exception 0xC0000005 +00/41/CMainGameComponent_SetDisplayMode_0041641f_test.cpp pass fail fail COMPONENT_DISPLAY_MODE_FAIL case=1 seq=7 errors=5 +00/43/CDisplayEngine_ChangeTextureColourDepth_00434dc0_test.cpp pass pass fail +00/43/CDisplayEngine_ConfirmFeedbackGameEventPackage_00434a60_test.cpp pass pass fail +00/43/CDisplayEngine_PostChangeResolution_00434a20_test.cpp pass pass fail +00/43/CDisplayEngine_PreChangeResolution_00434a10_test.cpp pass pass fail +00/43/CDisplayEngine_ReceiveFeedbackGameEvent_00434a30_test.cpp pass pass fail +00/43/CPlayerGui_GetPHeroInfoScreensManager_00437cd0_test.cpp pass pass crash exception 0xC0000005 +00/48/ByteVector_InsertFill_00485f3a_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\48\../../../src/compiled/00/48/ByteVector_InsertFill_00485f3a.cpp(23): error C2485: 'naked': unrecognized extended attribute +00/48/CPersistContextReadSection_CPersistContextReadSection_00485cd2_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\48\../../../src/compiled/00/48/CPersistContextReadSection_CPersistContextReadSection_00485cd2.cpp(14): error C2485: 'naked': unrecognized extended attribute +00/48/CPlayer_GetPControlledCreature_00487dc0_test.cpp pass pass fail +00/48/CPlayer_IsMainPlayer_00487520_test.cpp pass pass fail +00/48/CPlayer_PeekPControlledCreature_00487dd0_test.cpp pass pass fail +00/48/CPlayer_ToggleAggressiveMode_00487e90_test.cpp pass pass fail +00/48/CPlayer_UnfreezeControls_00487120_test.cpp pass pass fail +00/49/CFrontendGameComponent_ChooseProfileNumber_004962c0_test.cpp fail pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendGameComponent_ChooseProfileNumber_004962c0.cpp(36): error C2485: 'naked': unrecognized extended attribute +00/49/CFrontendGameComponent_ChooseSaveNumber_00494800_test.cpp fail pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendGameComponent_ChooseSaveNumber_00494800.cpp(32): error C2485: 'naked': unrecognized extended attribute +00/49/CFrontendGameComponent_ChooseWorldNumber_00494710_test.cpp fail pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendGameComponent_ChooseWorldNumber_00494710.cpp(45): error C2485: 'naked': unrecognized extended attribute +00/49/CFrontendGameComponent_ClearProfileResources_00495780_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendGameComponent_ClearProfileResources_00495780.cpp(20): error C2485: 'naked': unrecognized extended attribute +00/49/CFrontendGameComponent_CreateNewProfile_004945b0_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendGameComponent_CreateNewProfile_004945b0.cpp(51): error C2485: 'naked': unrecognized extended attribute +00/49/CFrontendGameComponent_Destroy_00495620_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendGameComponent_Destroy_00495620.cpp(28): error C2485: 'naked': unrecognized extended attribute +00/49/CFrontendGameComponent_GetMaxVisibleProfileNumber_00494520_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendGameComponent_GetMaxVisibleProfileNumber_00494520.cpp(19): error C2485: 'naked': unrecognized extended attribute +00/49/CFrontendGameComponent_GetMaxVisibleProfileNumber_00494680_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendGameComponent_GetMaxVisibleProfileNumber_00494680.cpp(19): error C2485: 'naked': unrecognized extended attribute +00/49/CFrontendGameComponent_GetMaxVisibleProfileNumber_00494770_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendGameComponent_GetMaxVisibleProfileNumber_00494770.cpp(19): error C2485: 'naked': unrecognized extended attribute +00/49/CFrontendGameComponent_InitGraphics_00494840_test.cpp fail pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendGameComponent_InitGraphics_00494840.cpp(120): error C2485: 'naked': unrecognized extended attribute +00/49/CFrontendGameComponent_PopulateProfiles_00495890_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendGameComponent_PopulateProfiles_00495890.cpp(39): error C2485: 'naked': unrecognized extended attribute +00/49/CFrontendGameComponent_SetActiveInputSlot_00494440_test.cpp pass pass fail CFrontendGameComponent_SetActiveInputSlot_00494440_test FAIL +00/49/CFrontendGameComponent_SetMode_00495fe0_test.cpp pass pass fail CFrontendGameComponent_SetMode_00495fe0_test FAIL +00/49/CFrontendGameComponent_ShowMoreProfiles_00494560_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendGameComponent_ShowMoreProfiles_00494560.cpp(19): error C2485: 'naked': unrecognized extended attribute +00/49/CFrontendGameComponent_ShowMoreProfiles_004946c0_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendGameComponent_ShowMoreProfiles_004946c0.cpp(19): error C2485: 'naked': unrecognized extended attribute +00/49/CFrontendGameComponent_ShowMoreProfiles_004947b0_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendGameComponent_ShowMoreProfiles_004947b0.cpp(19): error C2485: 'naked': unrecognized extended attribute +00/49/CFrontendProfileList_Refresh_00495830_test.cpp fail pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CFrontendProfileList_Refresh_00495830.cpp(27): error C2485: 'naked': unrecognized extended attribute +00/49/CWorld_IsHeroStateFileValid_0049d4e0_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\49\../../../src/compiled/00/49/CWorld_IsHeroStateFileValid_0049d4e0.cpp(32): error C2485: 'naked': unrecognized extended attribute +00/49/CWorld_SetAsPaused_0049d8f0_test.cpp pass pass fail +00/4a/CSymbolMap_ContainerLifetime_004ad4a0_test.cpp pass compile_fail compile_fail D:\Documents\FableTLC\rebuild\src\compiled\00\4a\CSymbolMap_ContainerLifetime_004ad4a0.cpp(12): error C2995: 'CVectorMap>::~ +00/4a/CWorld_LoadHeroStateInternal_004a2d70_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\4a\../../../src/compiled/00/4a/CWorld_LoadHeroStateInternal_004a2d70.cpp(50): error C2485: 'naked': unrecognized extended attribute +00/4a/CWorld_PrepareForLoad_004a0220_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\4a\../../../src/compiled/00/4a/CWorld_PrepareForLoad_004a0220.cpp(42): error C2485: 'naked': unrecognized extended attribute +00/4b/CQuestManager_DeactivateAllQuests_004b4450_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\4b\../../../src/compiled/00/4b/CQuestManager_DeactivateAllQuests_004b4450.cpp(16): error C2485: 'naked': unrecognized extended attribute +00/4c/CThing_PeekDefinitionManager_004c7990_test.cpp pass pass fail +00/4c/CThing_SetCurrentThingManager_004c74f0_test.cpp pass pass fail +00/52/CObserver_ObserveEvent_0052da20_test.cpp fail pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\52\../../../src/compiled/00/52/CObserver_ObserveEvent_0052da20.cpp(31): error C2485: 'naked': unrecognized extended attribute +00/53/CList_DoRecomputeOffsets_0053c332_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\53\../../../src/compiled/00/53/CList_DoRecomputeOffsets_0053c332.cpp(34): error C2485: 'naked': unrecognized extended attribute +00/53/CList_InitialiseOffsets_00536b4f_test.cpp pass crash compile_fail D:\Documents\FableTLC\rebuild\tests\00\53\../../../src/compiled/00/53/CList_InitialiseOffsets_00536b4f.cpp(26): error C2485: 'naked': unrecognized extended attribute +00/54/CFrontEndList_ScrollDown_0054c810_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\54\../../../src/compiled/00/54/CFrontEndList_ScrollDown_0054c810.cpp(236): error C2485: 'naked': unrecognized extended attribute +00/55/CTable_Initialise_00550c60_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\55\../../../src/compiled/00/55/CTable_Initialise_00550c60.cpp(15): error C2485: 'naked': unrecognized extended attribute +00/59/CFrontEndManager_CreateFrontEndButtonComponent_00596a68_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\59\../../../src/compiled/00/59/CFrontEndManager_CreateFrontEndButtonComponent_00596a68.cpp(90): error C2485: 'naked': unrecognized extended attribute +00/59/CFrontEndManager_GotoSpecificPreviousScreen_0059686d_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\59\../../../src/compiled/00/59/CFrontEndManager_GotoSpecificPreviousScreen_0059686d.cpp(120): error C2485: 'naked': unrecognized extended attribute +00/59/CFrontEndManager_LaunchVirtualKeyboard_00596917_test.cpp fail pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\59\../../../src/compiled/00/59/CFrontEndManager_LaunchVirtualKeyboard_00596917.cpp(54): error C2485: 'naked': unrecognized extended attribute +00/59/CFrontEndManager_RefreshAvailableProfilesForDelete_00597006_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\59\../../../src/compiled/00/59/CFrontEndManager_RefreshAvailableProfilesForDelete_00597006.cpp(85): error C2485: 'naked': unrecognized extended attribute +00/59/CFrontEndManager_RefreshAvailableProfiles_00596cce_test.cpp fail pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\59\../../../src/compiled/00/59/CFrontEndManager_RefreshAvailableProfiles_00596cce.cpp(74): error C2485: 'naked': unrecognized extended attribute +00/59/FrontEndDequeIterator_Distance_0059a8f3_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\59\../../../src/compiled/00/59/FrontEndDequeIterator_Distance_0059a8f3.cpp(20): error C2485: 'naked': unrecognized extended attribute +00/59/FrontEndHistoryDequeIterator_DistanceThunk_0059aae3_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\59\../../../src/compiled/00/59/FrontEndHistoryDequeIterator_DistanceThunk_0059aae3.cpp(24): error C2485: 'naked': unrecognized extended attribute +00/59/FrontEndHistoryDeque_FreeBlocks_0059aa64_test.cpp fail pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\59\../../../src/compiled/00/59/FrontEndHistoryDeque_FreeBlocks_0059aa64.cpp(12): error C2485: 'naked': unrecognized extended attribute +00/62/CWorld_EAApplyAllScriptedMapBrushes_00629210_test.cpp pass pass fail +00/62/CWorld_EACloseHeroInformationScreen_0062b3f0_test.cpp pass pass crash exception 0xC0000005 +00/62/CWorld_EACloseInGameMenu_00629d60_test.cpp pass pass crash exception 0xC0000005 +00/62/CWorld_EACloseLiveGUI_00629da0_test.cpp pass pass crash exception 0xC0000005 +00/62/CWorld_EAClosePhotoCapture_0062b4d0_test.cpp pass pass crash exception 0xC0000005 +00/62/CWorld_EAConsoleToggleState_0062a110_test.cpp pass pass link_fail CWorld_EAConsoleToggleState_0062a110.obj : error LNK2019: unresolved external symbol "public: __cdecl CConsole::CConsole(void)" (??0CConsole@@QEAA@XZ) referenced in function "public: void __cdecl CWor +00/62/CWorld_EAControlledCreatureBlockEnd_0062b000_test.cpp pass pass fail +00/62/CWorld_EAControlledCreatureDefault_006298f0_test.cpp pass pass fail +00/62/CWorld_EACreatureCheat_006292a0_test.cpp pass pass crash exception 0xC0000374 +00/62/CWorld_EADeleteMenuComponents_0062c980_test.cpp pass pass fail +00/62/CWorld_EADeleteTradeActiveMenu_0062c9d0_test.cpp pass pass fail +00/62/CWorld_EAMoveHeroToRegion_00629260_test.cpp pass pass fail +00/62/CWorld_EAOpenPCBuyTradingMenu_0062c7f0_test.cpp pass pass fail +00/62/CWorld_EAOpenPCInventoryMenu_0062c700_test.cpp pass pass fail +00/62/CWorld_EAOpenPCMapMenu_0062c8e0_test.cpp pass pass fail +00/62/CWorld_EAOpenPCOptionsMenu_0062c750_test.cpp pass pass fail +00/62/CWorld_EAOpenPCPhotoJournalMenu_0062c7a0_test.cpp pass pass fail +00/62/CWorld_EAOpenPCScoreboard_0062c640_test.cpp pass pass fail +00/62/CWorld_EAOpenPCSellTradingMenu_0062c840_test.cpp pass pass fail +00/62/CWorld_EAOpenPCSkillsMenu_0062c690_test.cpp pass pass fail +00/62/CWorld_EAOpenPCStatusMenu_0062c930_test.cpp pass pass fail +00/62/CWorld_EAOpenPCWantedTradingMenu_0062c890_test.cpp pass pass fail +00/62/CWorld_EARemoveCurrentMode_0062c4b0_test.cpp pass pass fail +00/62/CWorld_EASetFreeCameraMode_00629390_test.cpp pass pass fail +00/62/CWorld_EASetPauseMode_006292d0_test.cpp pass pass fail +00/62/CWorld_EASetSlowMotion_00629330_test.cpp pass pass fail +00/62/CWorld_EAToggleKillEverythingMode_00629570_test.cpp pass pass fail +00/62/CWorld_EAUpdateFrame_00629270_test.cpp pass pass fail +00/62/CWorld_EAUseFreeCamera_00629420_test.cpp pass pass fail +00/63/CPlayer_GetCurrentMode_00633be0_test.cpp pass pass fail +00/66/CDefinitionManager_GetDefCombatTypeIndex_00669640_test.cpp pass pass fail +00/66/CDefinitionManager_GetDefCombatTypeName_00669770_test.cpp pass pass fail +00/66/CFrontEndManager_CreateComponent_00661d20_test.cpp fail pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\66\../../../src/compiled/00/66/CFrontEndManager_CreateComponent_00661d20.cpp(41): error C2485: 'naked': unrecognized extended attribute +00/66/CThingCreatureBase_GetCombatTypeDef_00666d10_test.cpp pass pass crash exception 0xC0000005 +00/66/CThingCreatureBase_GetPCombatTypeDef_00664d40_test.cpp pass pass crash exception 0xC0000005 +00/66/HeroInformationManager_Close_0066e73d_test.cpp pass pass crash exception 0xC0000005 +00/69/CGameCameraManager_RecentreCamera_006978c0_test.cpp pass pass fail +00/73/CTCCombat_IsAbleToBeKnockedDown_007331a0_test.cpp pass pass crash exception 0xC0000005 +00/73/CTCCombat_UnsetBlocking_007338a0_test.cpp pass pass crash exception 0xC0000005 +00/73/CTCCreatureModeManager_RemoveMode_0073aba0_test.cpp pass pass crash exception 0xC0000005 +00/85/CWorld_LEASetDisplayMode_0085caf0_test.cpp pass pass fail +00/85/CWorld_LEASetEditorMode_0085cb80_test.cpp pass pass fail +00/85/CWorld_LEASetExclusiveMode_0085ca80_test.cpp pass pass fail +00/98/CShaderRenderManager_UpdateCombinedProjectionTransform_00988a50_test.cpp pass pass crash exception 0xC0000005 +00/99/CCharString_AllocStringData_0099ea60_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\99\CCharString_AllocStringData_0099ea60_test.cpp(45): error C2821: first formal parameter to 'operator new' must be 'size_t' +00/99/CDataInputStream_ReadWithSrcChunkOverflow_00993ca0_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\99\../../../src/compiled/00/99/CDataInputStream_ReadWithSrcChunkOverflow_00993ca0.cpp(8): error C2485: 'naked': unrecognized extended attribute +00/99/CFileDataInputStream_CFileDataInputStream_00994700_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\99\../../../src/compiled/00/99/CFileDataInputStream_CFileDataInputStream_00994700.cpp(19): error C2485: 'naked': unrecognized extended attribute +00/99/CFileDataInputStream_dtor_00994780_test.cpp pass pass compile_fail D:\Documents\FableTLC\rebuild\tests\00\99\../../../src/compiled/00/99/CFileDataInputStream_dtor_00994780.cpp(15): error C2485: 'naked': unrecognized extended attribute +00/9a/CDefinitionManager_GetPDefFromGlobalIndexConst_009ad9e0_test.cpp pass pass crash exception 0xC0000005 +00/9a/CDefinitionManager_GetPDefFromGlobalIndex_009ada10_test.cpp pass pass crash exception 0xC0000005 +00/9a/CDefinitionManager_GetPDefFromInstantiationNameConst_009ada40_test.cpp pass pass crash exception 0xC0000005 +00/9a/CDefinitionManager_GetPDefFromInstantiationName_009ada90_test.cpp pass pass crash exception 0xC0000005 +00/9b/CCountedPointer_CSymbolMap_Reset_009b1f50_test.cpp pass compile_fail compile_fail D:\Documents\FableTLC\work\modern_behavior\include_retail_bitset\../src/compiled/00/4a/CSymbolMap_ContainerLifetime_004ad4a0.cpp(12): error C2995: 'CVectorMap *,str +00/9b/STL_Sort_SubDefEntry_009b84c0_test.cpp pass pass link_fail STL_Sort_SubDefEntry_009b84c0.obj : error LNK2019: unresolved external symbol "void __cdecl _STL::__introsort_loop *,struct _STL::pair +00/9c/CBankFile_EntryExists_009cc190_test.cpp pass pass fail +00/9d/CBankFileAsync_Close_009d58a0_test.cpp pass pass crash exception 0xC0000005 +00/9d/CBankFileAsync_ProcessDisposalList_009d6730_test.cpp pass pass crash exception 0xC0000005 +00/9d/CBankFileAsync_RestartAbortedReads_009d5a90_test.cpp pass pass crash exception 0xC0000005 +00/9d/CDefStringTable_CDefStringTable_009d4db0_test.cpp pass pass fail global +00/9e/CFontManager_CFontManager_009e2e30_test.cpp pass compile_fail compile_fail D:\Documents\FableTLC\work\modern_behavior\include_retail_bitset\../src/compiled/00/4a/CSymbolMap_ContainerLifetime_004ad4a0.cpp(12): error C2995: 'CVectorMap >::~list>::~CVectorMap(void)': function t | +| `00/56/CTCInventoryExperience_GetOriginalExperience_0056ea9a.cpp` | C2039 | C2039 | CTCInventoryExperience_GetOriginalExperience_0056ea9a.cpp:5 C2039 'GetOriginalExperience': is not a member of 'CTCInventoryExperience' | +| `00/57/CTCHeroStats_HasCurrentMarriage_0057ac4b.cpp` | C2039 | C2039 | CTCHeroStats_HasCurrentMarriage_0057ac4b.cpp:4 C2039 'HasCurrentMarriage': is not a member of 'CTCHeroStats' | +| `00/64/CDrawMiniMap_ClearPrimitivesIfNecessary_00643de5.cpp` | C2039 | C2039 | CDrawMiniMap_ClearPrimitivesIfNecessary_00643de5.cpp:14 C2039 'sub': is not a member of 'CDrawMiniMap' | +| `00/99/DataInputStream_ReadWithSrcChunkOverflow_00993ca0.cpp` | C2664 | C2664 | DataInputStream_ReadWithSrcChunkOverflow_00993ca0.cpp:26 C2664 'void CDataInputStream::InternalReadNextSourceChunk(const fable_u8 *&,long &)': cannot convert argument 1 from 'fable_u8 | +| `00/9f/009f9ee0.pre.cpp` | C2065 | C2065 | 009f9ee0.pre.cpp:67 C2065 'CPixelFormatByteLengthView': undeclared identifier | +| `00/a7/CNavQuadTree_InitialiseLines_00a7a5e0.cpp` | C2664 | C2664 | CNavQuadTree_InitialiseLines_00a7a5e0.cpp:20 C2664 'void CNavQuadTree::UpdateLines(CNavLineCellVector &,const std::vector> &,const C2DBox | +| `00/ab/CVertexShaderInput_GenerateD3DDeclaration_00ab3870.cpp` | C2065 | C2065 | CVertexShaderInput_GenerateD3DDeclaration_00ab3870.cpp:50 C2065 'stream': undeclared identifier | +| `00/b5/CEngineWeatherRenderer_AddWeatherMask_00b50c80.cpp` | C2039 | C2039 | CEngineWeatherRenderer_AddWeatherMask_00b50c80.cpp:3 C2039 'AddWeatherMask': is not a member of 'CEngineWeatherRenderer' | +| `00/b5/CEngineWeatherRenderer_ClipLockedBoxToCamera_00b52120.cpp` | C2039 | C2039 | CEngineWeatherRenderer_ClipLockedBoxToCamera_00b52120.cpp:3 C2039 'ClipLockedBoxToCamera': is not a member of 'CEngineWeatherRenderer' | +| `00/b8/CEngineSubPrimitiveRenderer_RadixSort_00b847d0.cpp` | C2065 | C2065 | CEngineSubPrimitiveRenderer_RadixSort_00b847d0.cpp:16 C2065 'i': undeclared identifier | +| `00/b8/CEngineSubPrimitiveRenderer_SortSingleRadix_00b84b20.cpp` | C2065 | C2065 | CEngineSubPrimitiveRenderer_SortSingleRadix_00b84b20.cpp:8 C2065 'i': undeclared identifier | +| `00/b9/CEngineInternalPrimitiveStaticMesh_GetWorldPosition_00b9d1e0.cpp` | C2039 | C2039 | CEngineInternalPrimitiveStaticMesh_GetWorldPosition_00b9d1e0.cpp:3 C2039 'GetWorldPosition': is not a member of 'CEngineInternalPrimitiveStaticMesh' | +| `00/be/CEnginePrimitive2DViewportManager_CreateInternalPrimitive_00be2260.cpp` | clean | C2371 | CEnginePrimitive2DViewportManager_CreateInternalPrimitive_00be2260.cpp:7 C2371 'size_t': redefinition; different basic types | + +## x64 layout drift (`--layouts`) + +Each of the 1044 `rebuild/include/engine/*.h` headers compiled alone for x64 with its +PDB asserts live: **1045 of 1499** size-asserted classes fail (vtables or +pointer members). The fork needs its own types for these; persistence is unaffected because +saves and defs go member by member ([PERSISTED_TYPES](PERSISTED_TYPES.md)). List: +[X64_LAYOUT_DRIFT.tsv](X64_LAYOUT_DRIFT.tsv). + +## x86 layout drift (`--layouts --arch x86`) + +The same headers compiled for **x86** with the current compiler: **0 of 1505** +size-asserted classes change size at the same pointer width, with 0 headers failing otherwise. Any class listed in +[X86_LAYOUT_DRIFT.tsv](X86_LAYOUT_DRIFT.tsv) embeds a VC7.1 standard-library type whose current layout differs. +The generated engine headers model library members by their PDB size, so they do not depend on the +current library. The step-1 layout breaks seen in the TU pass come from hand-written shared headers +(`std::bitset<274>` in `fable_world.h`). diff --git a/docs/modern/MODERN_COMPILE_CENSUS.tsv b/docs/modern/MODERN_COMPILE_CENSUS.tsv new file mode 100644 index 0000000000..52144838c1 --- /dev/null +++ b/docs/modern/MODERN_COMPILE_CENSUS.tsv @@ -0,0 +1,2249 @@ +tu x86 x86_detail x64 x64_detail first_error +00/40/0040bb90.pre.cpp clean ptr_warning C4312 +00/40/CCountedPointer_PhysicsFacesSegment_Reset_00403e40.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CCountedPointerFontBank_ReleaseData_004057a0.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CDisplayManagerInit_CDisplayManagerInit_004016f0.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CFileInstallerSingleton_Get_00404440.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/40/CFileInstallerSingleton_Release_00404490.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/40/CIME_Initialise_00405650.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CIME_LoadIMESettings_00405350.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CIME_Render_00404c00.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CIMEFont_DrawTextW_00405020.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CIMEFont_GetFontNameAndScale_00404970.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CIMEFont_GetTextExtent_00404f60.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CMemoryFile_IsOpen_00405d30.cpp clean ptr_warning C4302,C4311,C4312 +00/40/ConsvalstdallocatorstdpairCWideStringconstfloatstdpairCWideStringconstfloatstd_pairCWideStringfloat_0040dd00.cpp clean inline_asm +00/40/CPersistContext_BoolTransferIn_00403fc0.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CPersistContext_BoolTransferOut_00403f90.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CPersistContext_ByteTransferIn_004059f0.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CPersistContext_ByteTransferOut_00405a10.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CPersistContext_CheckCRC_00404500.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CPersistContext_Transferbool_004045c0.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CPersistContext_TransferSignedChar_00405a60.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CPersistTraitsBool_TransferIn_00403ff0.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CPersistTraitsByte_TransferBinaryIn_00405940.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CPersistTraitsLong_TransferBinaryIn_0040fe60.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CStringParser_Constructor_00404720.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CStringParser_CStringParser_00404720.cpp clean ptr_warning C4302,C4311 +00/40/CStringParserCommentList_Destructor_004046b0.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CSystemManagerInit_Constructor_00403b10.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CUIScreenUnitConverter_ResetDimensions_0040f0e0.cpp clean ptr_warning C4302,C4311,C4312 +00/40/CUserProfileManager_Apply_0040bc80.cpp clean error C2821 fable_user_profile.h:12 C2821 first formal parameter to 'operator new' must be 'size_t' +00/40/CUserProfileManager_Get_0040d2a0.cpp clean error C2821 fable_user_profile.h:12 C2821 first formal parameter to 'operator new' must be 'size_t' +00/40/CUserProfileManager_GetDebugSaveDir_00406780.cpp clean error C2821 fable_user_profile.h:12 C2821 first formal parameter to 'operator new' must be 'size_t' +00/40/CUserProfileManager_SetProfileName_0040d400.cpp clean error C2821 fable_user_profile.h:12 C2821 first formal parameter to 'operator new' must be 'size_t' +00/40/CVectorMapThingComponents_LowerBound_0040f020.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/40/EULA_DoEULAThings_00401fe0.cpp clean ptr_warning C4302,C4311,C4312 +00/40/FileInstallerCountedReset_00403db0.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/40/FileInstallerDestroy_00403630.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/40/GFMain_00402510.cpp clean error C2821 fable_user_profile.h:12 C2821 first formal parameter to 'operator new' must be 'size_t' +00/40/Global_Array_LinearSearchInt_0040e170.cpp clean inline_asm +00/40/global_BufferRellocateAndUpdateWritePointer_0040f390.cpp clean ptr_warning C4302,C4311,C4312 +00/40/Global_CCharStringComputeCRC32_00404310.cpp clean inline_asm +00/40/Global_CUserProfileManagerSaveAndCleanup_0040bb90.cpp clean ptr_warning C4312 +00/40/Global_E2_00404a90.cpp clean inline_asm +00/40/Global_Exit_00401284.cpp clean inline_asm +00/40/Global_GetWindowTitle_004023f0.cpp clean ptr_warning C4302,C4311,C4312 +00/40/Global_GFConfigDetection_004017b0.cpp clean ptr_warning C4302,C4311,C4312 +00/40/Global_GFGetBuildNumber2_00401f30.cpp clean ptr_warning C4302,C4311,C4312 +00/40/Global_GFHandleSystemInitError_00401c00.cpp clean ptr_warning C4302,C4311,C4312 +00/40/global_GFInitialise_004022b0.cpp clean error C2821 fable_boot.h:82 C2821 first formal parameter to 'operator new' must be 'size_t' +00/40/Global_GFIsSystemErrorFailure_00401bc0.cpp clean ptr_warning C4302,C4311,C4312 +00/40/Global_GFUninitialise_00401b80.cpp clean ptr_warning C4302,C4311,C4312 +00/40/Global_MyDocuments_CheckWritePermissions_00402130.cpp clean ptr_warning C4302,C4311,C4312 +00/40/Global_SEHEpilog_004013c7.cpp clean inline_asm +00/40/Global_SEHProlog_0040138c.cpp clean inline_asm +00/40/Global_WinMain_00403480.cpp clean error C2821 fable_boot.h:82 C2821 first formal parameter to 'operator new' must be 'size_t' +00/40/ProgressCountedConstructor_00403d20.cpp clean error C2821 fable_boot.h:82 C2821 first formal parameter to 'operator new' must be 'size_t' +00/40/ProgressDestroy_00403600.cpp clean error C2821 fable_boot.h:82 C2821 first formal parameter to 'operator new' must be 'size_t' +00/40/TransferIMEColour_00405220.cpp clean ptr_warning C4302,C4311,C4312 +00/40/UserProfileManagerCountedReset_0040f1b0.cpp clean error C2821 fable_user_profile.h:12 C2821 first formal parameter to 'operator new' must be 'size_t' +00/40/UserProfileManagerDestroy_0040d830.cpp clean error C2821 fable_boot.h:82 C2821 first formal parameter to 'operator new' must be 'size_t' +00/41/C2DBoxF_Move_0041a810.cpp clean inline_asm +00/41/CAFile_GetFileInformation_00412090.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CBasicStringchar_CBasicStringCharoperatorequals_004115a0.cpp clean ptr_warning C4302,C4311 +00/41/CCompressedMemoryDataInputStream_CCompressedMemoryDataInputStream_00414f90.cpp clean inline_asm +00/41/CConsoleClasslessCommand_CallCommandFunction_00415040.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CConsoleCommandBase_GetTypeAsString_00414f70.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CConsoleCommandBase_GetTypeAsStringCConsoleCommandBaseMB_00414f70.cpp clean inline_asm +00/41/CConsoleVariable_GetTypeAsString_00413700.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CCountedFontPointer_Sharing_004190e2.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CCountedPointerCIPhysicsFacesSegmentBaseconst_Reset_00413590.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CCountedPointerConsole_Reset_00413520.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CCountedPointerFontBank_Reset_0041947a.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CCountedPointerstdvectorCCharString_Reset_00419428.cpp clean inline_asm +00/41/CCountedPointerstdvectorCCharString_Reset_004194d1.cpp clean inline_asm +00/41/CCountedPointerstdvectorCCharString_Reset_004195af.cpp clean inline_asm +00/41/CDefString_ToCharStringTyped_00415dd0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CDisplayEngineInit_CDisplayEngineInit_00419270.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CEngineSceneGrid_GetCellBoundingBox_004107c0.cpp clean ptr_warning C4302,C4311 +00/41/CFrameRateSmoother_0CFrameRateSmoother_00419747.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CGame_Initialise_00413120.cpp clean error C2821 fable_boot.h:82 C2821 first formal parameter to 'operator new' must be 'size_t' +00/41/CGame_Play_00412f90.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CGameComponent_Init_00415920.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CGameComponent_SetQuit_00415930.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CGameEventPackage_CGameEventPackage_00419220.cpp clean inline_asm +00/41/CGraphicFrame_UnInitialise_0041c700.cpp clean inline_asm +00/41/CGraphicFrameData_scalardeletingdestructor_0041bdb0.cpp clean inline_asm +00/41/CGraphicFrameData_scalardeletingdestructor_0041bdf0.cpp clean inline_asm +00/41/CInitBaseClass_Uninitialise_00412f60.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CInterpolationInfo_CInterpolationInfo_00415a60.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CLandscapeBackgroundPatch_vectordeletingdestructor_00415940.cpp clean inline_asm +00/41/CMainGameComponent_1CMainGameComponent_00418b7a.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_AddEventPackageSetToSave_004161a7.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_AddGameEvent_0041649c.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_AppReinitFunc_004167da.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_BeginInputLoading_0041627f.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_BeginInputSaving_00416268.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_ChangeTextureColourDepth_0041799b.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_CheckSync_004165e8.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_CMainGameComponent_00418dca.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_CreatePlayers_004166a8.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_EventIsSystemEvent_00415ff2.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_ForceRender_004164e0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_GCMainGameComponent_004197b3.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_GenerateMetFilesFromLugFiles_00418c3b.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_GetAverageFrameDuration_00416219.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_GetCurrentGameTime_00416231.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_GetEventPackageSetFromSave_00416148.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_GetGameTimeOfNextPresentCompletion_004166e2.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_GetInputs_00416e78.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_GetPredictedTimeSinceLastRenderFrame_00416258.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_Init_004184bd.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_InitDefinitions_00416005.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/41/CMainGameComponent_InitDisplayEngine_00417418.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_InitEventPackageLoading_00416056.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_InitEventPackageSaving_004160cc.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_InitGraphics_00416c8a.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_InitialiseFonts_004168dc.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_InitialiseText_00416832.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_InitParticleEngine_004174f1.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_InitPlayerManager_0041732a.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_InitWorld_0041735a.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_IsControllerDisconnected_00416296.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_IsEditorActive_004197b0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_IsTimeForServerUpdate_0041674a.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_OnPostDeviceReset_004175da.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_OnPreDeviceReset_00415e83.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_PeekWorldChecksum_00416392.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_PostChangeResolution_00416365.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_PostInit_00416953.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_ProcessEvent_00415fe0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_ProcessEventPackage_00416670.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_Render_00417001.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_Run_004189c2.cpp layout fable_world.h error C2821 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_SetDisplayMode_0041641f.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_SetExclusiveMode_004163b7.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_SetQuit_004197a0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_Shutdown_004175e5.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_SignalFrameUpdate_00416047.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/41/CMainGameComponent_UninitEventPackageLoading_004160a6.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_UninitEventPackageSaving_00416122.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_Update_00418289.cpp layout fable_world.h error C4235 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_UpdateAverageFrameDuration_00416202.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponent_UpdateForcedTickSpeed_00417747.cpp layout fable_world.h error C4235 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_UpdateFromEventPackageSet_0041726d.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/41/CMainGameComponent_UpdateRegular_004162b5.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMainGameComponentInit_CMainGameComponentInit_004131a0.cpp clean inline_asm +00/41/CMainGameComponentInit_CMainGameComponentInit_00415e17.cpp clean inline_asm +00/41/CManager_ClearErrorMessage_0041ce6a.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CManager_GetInstance_0041e5f2.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CManager_IsKeyHeld_0041e16e.cpp clean inline_asm +00/41/CManager_SetGraphicsBank_0041d198.cpp clean inline_asm +00/41/CManager_UpdateErrorMessage_0041ce9d.cpp clean inline_asm +00/41/CMemoryFile_GetLength_00412060.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CMemoryFile_GetPosition_00412050.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CObject_CObject_0041c4a0.cpp clean inline_asm +00/41/CSprite_GetDefaultGraphicIndex_0041ab00.cpp clean inline_asm +00/41/CSurface_GetHeight_00415bf0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/CTCBoastingCrowdControl_OnKillCrowd_0041d1c8.cpp clean inline_asm +00/41/CTCBoastingCrowdControl_OnKillCrowd_0041d8a8.cpp clean inline_asm +00/41/CThing_GetPBaseDef_00419650.cpp clean inline_asm +00/41/CUserProfileManager_GetAutoSaveFileName_0041a080.cpp clean ptr_warning C4302,C4311,C4312 +00/41/Destroyrangestd_allocatorCSpeechFrame_00414e00.cpp clean ptr_warning C4302,C4311,C4312 +00/41/DestvalstdallocatorstdpairlongCMusicManagerCRegisteredMusicEntrystdpairlongCMusicManager_CRegisteredMusicEntry_004167a7.cpp clean inline_asm +00/41/global_ConsoleForceUpdateTickSpeed_00416c35.cpp clean inline_asm +00/41/global_Get_00414c90.cpp clean inline_asm +00/41/Global_GetAutoSaveCheckpointDirectory_0041a4c0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/Global_GetCacheDirectory_0041a180.cpp clean ptr_warning C4302,C4311,C4312 +00/41/Global_GetGraphicsDirectory_0041a100.cpp clean ptr_warning C4302,C4311,C4312 +00/41/Global_GetLanguageDirectoryA_0041a120.cpp clean ptr_warning C4302,C4311,C4312 +00/41/Global_GetLanguageDirectoryB_0041a330.cpp clean ptr_warning C4302,C4311,C4312 +00/41/Global_GetMiscDirectoryA_0041a0a0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/Global_GetMiscDirectoryB_0041a0c0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/Global_GetShadersDirectory_0041a1e0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/Global_GetStreamingFontBankName_00415440.cpp clean ptr_warning C4302,C4311,C4312 +00/41/global_GFLimitReturndouble_0041919c.cpp clean ptr_warning C4302,C4311,C4312 +00/41/Global_InitialiseConsoleVariables_00413c50.cpp clean ptr_warning C4302,C4311,C4312 +00/41/global_OnCloseMenus_0041d165.cpp clean inline_asm +00/41/global_OnOpenMenus_0041d122.cpp clean inline_asm +00/41/global_ShowErrorMessage_0041e61b.cpp clean ptr_warning C4302,C4311,C4312 +00/41/global_VectorPushBack_004128a0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NLocalisation_GetLanguage_00415070.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NLocalisation_GetLanguageFolder_00415530.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NLocalisation_GetTextBankName_00415260.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NParticleEngineCAEnvironmentInteraction_GetGravityVector_00415cd0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NParticleEngineCAEnvironmentInteraction_GetVectorParam_00415cf0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NParticleEngineCAEnvironmentInteraction_IsWindActive_00415cc0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NResourceDirectoryNames_DataDirs_0041a0e0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NResourceDirectoryNames_EngineDirectories_0041a1c0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NResourceDirectoryNames_FixedDirs_0041a0a0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NResourceDirectoryNames_GetDebugSaveDir_0041a1a0.cpp clean error C2821 fable_user_profile.h:12 C2821 first formal parameter to 'operator new' must be 'size_t' +00/41/NResourceDirectoryNames_GetDVDDialogueDir_0041a2d0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NResourceDirectoryNames_GetGraphicBankDir_0041a100.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NResourceDirectoryNames_GetHDMiscDir_0041a0c0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NResourceDirectoryNames_GetLevelDir_0041a060.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NResourceDirectoryNames_GetMyDocumentsDir_0041a430.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NResourceDirectoryNames_GetSaveDir_0041a540.cpp clean ptr_warning C4302,C4311,C4312 +00/41/NResourceDirectoryNames_GetShaderBankDir_0041a1e0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/ParticleEnvironmentPointer_Ctor_0041956c.cpp clean ptr_warning C4302,C4311,C4312 +00/41/STLConsoleParameters_vector_00419f50.cpp clean ptr_warning C4302,C4311,C4312 +00/41/STLRbTree_Extrema_0041c7e0.cpp clean ptr_warning C4302,C4311,C4312 +00/41/vectorCBrainBehaviour_resize_004129f0.cpp clean inline_asm +00/41/vectorEWeaponClass_UmoveEWeaponClass_00412aa0.cpp clean inline_asm +00/41/vectorEWeaponClass_UmoveEWeaponClass_00412c10.cpp clean inline_asm +00/41/vectorstdpairCCharString_resize_00412b20.cpp clean inline_asm +00/41/vectorunsignedint_resize_00412a50.cpp clean inline_asm +00/42/CAnimFilterNoGroup_Clone_00429a2f.cpp clean inline_asm +00/42/CBankFile_GetBankHandle_0042bed4.cpp clean inline_asm +00/42/CCountedPointerConstFontBank_ReleaseData_0042f948.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CCountedPointerstdvectorCCharString_Reset_00429c6f.cpp clean inline_asm +00/42/CCountedPointerstdvectorCCharString_Reset_0042fc13.cpp clean inline_asm +00/42/CDefClassBase_AddSubDef_0042da30.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CDefClassBase_GetInstantiationName_0042da10.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CDefClassBase_GetPSubDefInfo_0042da50.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CDefClassBase_GetPSubDefInfoConst_0042da60.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CDefClassBase_GetPSubDefInfoMap_0042daa0.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CDefClassBase_IsParentDef_0042da70.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CDefClassBase_PostTransfer_0042da80.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CDefClassBase_RemoveSubDef_0042da40.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CDefClassBase_SetInstantiationName_0042da20.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CDefClassBase_Validate_0042da90.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CEngineInit_Lifetime_0042d830.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CEngineInterpolationInfo_CEngineInterpolationInfo_0042fa30.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CEnginePrimitiveOcclusionManager_GetOccludedFrames_0042dae0.cpp clean inline_asm +00/42/CEngineSceneGrid_GetCellBoundingBox_0042b945.cpp clean inline_asm +00/42/CMemoryAllocatorVariableSize_GetNoAllocatedAreas_004292c0.cpp clean ptr_warning C4302,C4311 +00/42/CMemoryAllocatorVariableSize_RotateLeft_0042951b.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CMemoryAllocatorVariableSize_RotateLeft_0042955b.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_1CNewFrontendGameComponent_0042f50b.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_ChangeTextureColourDepth_0042dc5c.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_CleanUpInterfaces_0042dd28.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_CNewFrontendGameComponent_0042ea8f.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_CompileDefsPlatformSpecific_0042f5a9.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_Draw_0042df9e.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_Init2_0042e98f.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_Init_0042f75e.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_InitialiseDefs_0042f722.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_InitialiseEngine_0042e204.cpp clean inline_asm +00/42/CNewFrontendGameComponent_InitialiseGraphics_0042ddb3.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_InitialiseInput_0042dec5.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_Input_0042e3ee.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_IsControllerDisconnected_0042dc63.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_OnPostDeviceReset_0042dc60.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_OnPreDeviceReset_0042dc5f.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_ReleaseXMVCode_0042dbd8.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_Run_0042ec7c.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/42/CNewFrontendGameComponent_Shutdown_0042ebb6.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_Update_0042dc94.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CNewFrontendGameComponent_UpdateInterpolation_0042dbfa.cpp clean ptr_warning C4302,C4311,C4312 +00/42/CompareDataOffset_operator_0042c058.cpp clean inline_asm +00/42/ConsvalstdallocatorstdpairEHeroMorphTypeCParticleMorphsCEntrystdpairEHeroMorphTypeCParticleMorphsCEntrystdpairenumEHeroMorphTypeclassCParticleMorphs_CEntryconst_0042dbee.cpp clean inline_asm +00/42/CSurface_GetHeight_0042be11.cpp clean inline_asm +00/42/CTCInventoryClothing_SetClothingMappingToMenuEntry_0042d405.cpp clean inline_asm +00/42/CTCInventoryClothing_SetClothingMappingToMenuEntry_0042d41e.cpp clean inline_asm +00/42/CXMVPlayerBuffers_CXMVPlayerBuffers_0042db40.cpp clean ptr_warning C4302,C4311,C4312 +00/42/DestvalstdallocatorCIVCountedPointerN3DPrimitiveCTriangleListCIVCountedPointerN3DPrimitive_CTriangleList_0042fc66.cpp clean inline_asm +00/42/FindstdListuncheckediteratorstdListvalNUISystemCComponentstdallocatorNUISystemCComponentNUISystem_CComponent_00429418.cpp clean inline_asm +00/42/FindstdListuncheckediteratorstdListvalNUISystemCComponentstdallocatorNUISystemCComponentNUISystem_CComponent_004294ac.cpp clean inline_asm +00/42/FindstdListuncheckediteratorstdListvalNUISystemCComponentstdallocatorNUISystemCComponentNUISystem_CComponent_0042a77f.cpp clean inline_asm +00/42/global_Alloc_0042b587.cpp clean inline_asm +00/42/global_Alloc_0042bdf0.cpp clean inline_asm +00/42/global_Alloc_0042be2f.cpp clean inline_asm +00/42/global_CompileDefs_0042f70b.cpp clean ptr_warning C4302,C4311,C4312 +00/42/global_SetIsUsingRelativeCoords_004299a8.cpp clean inline_asm +00/42/global_StdDoubleLinkedListCreateNode_0042ab01.cpp clean ptr_warning C4302,C4311,C4312 +00/42/InsertstdListconstiteratorstdListvalCClassstd_allocatorCClass_0042b4c3.cpp clean inline_asm +00/42/listNUISystemCList_begin_00429a85.cpp clean inline_asm +00/42/mapCCharString_operator_0042d131.cpp clean inline_asm +00/42/mapNUISystemCComponent_operator_0042d1b7.cpp clean inline_asm +00/42/mapNUISystemCManagerEUsedKeys_operator_0042d201.cpp clean inline_asm +00/42/mapNUISystemCManagerEUsedKeys_operator_0042d246.cpp clean inline_asm +00/42/pairCCharString_swap_0042fb40.cpp clean inline_asm +00/42/STL_Mfind_0042b467.cpp clean ptr_warning C4302,C4311,C4312 +00/42/STL_Rb_global_M_decrement_004294e2.cpp clean ptr_warning C4302,C4311,C4312 +00/42/STL_Rb_global_Rebalance_0042971d.cpp clean ptr_warning C4302,C4311,C4312 +00/42/STLRbTree_RebalanceForErase_0042a34e.cpp clean ptr_warning C4302,C4311,C4312 +00/43/C3DAnimation2_CanRelocateData_00434d60.cpp clean inline_asm +00/43/CArraystdpairlong_pushback_0043332c.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CCompressedMemoryDataInputStream_CCompressedMemoryDataInputStream_004304a0.cpp clean inline_asm +00/43/CControlsDef_TransferCControlsDefUAEXAAVCPersistC_004330f0.cpp clean inline_asm +00/43/CCountedPointer_CStartupDefinitionManager_ReleaseData_00433cc0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CCountedPointer_CStartupDefinitionManager_Reset_00433d3a.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CCreatureActionFireMissileWeapon_FrameUpdate_0043318d.cpp clean inline_asm +00/43/CCreatureActionFireMissileWeapon_FrameUpdate_004331f9.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/43/CDefaultEngineMemoryInit_ApplyCustomValue_00437160.cpp clean inline_asm +00/43/CDefinitionManager_GetDefPlayerGuiName_0043ff30.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CDefinitionManager_GetDefVCEngineDef_00430096.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CDefinitionManager_GetDefVCFrontEndDef_004300fb.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CDisplayEngine_ChangeTextureColourDepth_00434dc0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CDisplayEngine_ConfirmFeedbackGameEventPackage_00434a60.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CDisplayEngine_Constructor_00434e10.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CDisplayEngine_Destructor_004354c0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CDisplayEngine_EndScreenFadeOut_00434c90.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CDisplayEngine_Init_004350d0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CDisplayEngine_InitEngine_00435190.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/43/CDisplayEngine_PostChangeResolution_00434a20.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CDisplayEngine_PreChangeResolution_00434a10.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CDisplayEngine_ReceiveFeedbackGameEvent_00434a30.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CDisplayEngine_Render_00435f70.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CDisplayEngine_RenderHeroDrunkenness_00435070.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/43/CDisplayEngine_RenderHeroRegion_00435000.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/43/CDisplayEngine_SetAboutToRun_00434cd0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CDisplayEngine_StartLetterBoxMode_00434ba0.cpp clean inline_asm +00/43/CDisplayEngine_WorldUpdate_00434f60.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CEngine_StartNewScene_00434840.cpp clean inline_asm +00/43/CEngineLightingManager_UpdateShadowScene_00431061.cpp clean inline_asm +00/43/CEngineLightingManager_UpdateShadowScene_00431102.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CEngineManager_Get_00436fb0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CEngineManager_Ownership_004366b0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CFontBank_GetVStringWidth_00437210.cpp clean inline_asm +00/43/CFontBank_GetVStringWidth_0043e4d0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CFrontEndDef_Alloc_00433080.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CFrontEndDef_Assign_00432e60.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CFrontEndDef_Copy_00432e50.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CFrontEndDef_GetSizeofClass_00431e30.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CFrontEndDef_TransferCFrontEndDefUAEXAAVCPersistC_004331c0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/43/CGameScriptInterface_GiveHeroExpression_004383d0.cpp clean inline_asm +00/43/CGuiControlTreePane_SortTreeRecursively_0043f230.cpp clean inline_asm +00/43/CGuiControlTreePane_SortTreeRecursively_0043fe40.cpp clean inline_asm +00/43/CLandscapeBackgroundPatch_vectordeletingdestructor_0043f720.cpp clean inline_asm +00/43/CLandscapeBackgroundPatch_vectordeletingdestructor_0043f750.cpp clean inline_asm +00/43/CLandscapeBackgroundPatch_vectordeletingdestructor_0043f920.cpp clean inline_asm +00/43/CLandscapeBackgroundPatch_vectordeletingdestructor_0043f980.cpp clean inline_asm +00/43/CNavigatorQueueManager_FrameUpdate_0043fdf0.cpp clean inline_asm +00/43/CNewFrontendGameComponent_GCNewFrontendGameComponent_004330d2.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CNewFrontendGameComponent_SetQuit_00430340.cpp clean ptr_warning C4302,C4311,C4312 +00/43/ConsvalstdallocatorstdpairCCharStringC3DVectorstdpairCCharStringC3DVectorstd_pairclassCCharStringclassC3DVector_0043f400.cpp clean inline_asm +00/43/ConsvalstdallocatorstdpairEHeroMorphTypeCParticleMorphsCEntrystdpairEHeroMorphTypeCParticleMorphsCEntrystdpairenumEHeroMorphTypeclassCParticleMorphs_CEntryconst_00434a00.cpp clean inline_asm +00/43/ConsvalstdallocatorstdpairEHeroMorphTypeCParticleMorphsCEntrystdpairEHeroMorphTypeCParticleMorphsCEntrystdpairenumEHeroMorphTypeclassCParticleMorphs_CEntryconst_00437820.cpp clean inline_asm +00/43/CopyimplCArrayCRandomAppearanceMorphCBodyPartMeshCArrayclassCRandomAppearanceMorph_CBodyPartMesh_00433cec.cpp clean inline_asm +00/43/CParentDefClassBase_GetInstantiationName_004303b0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CParentDefClassBase_GetPSubDefInfoMap_004303e0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CParentDefClassBase_IsParentDef_004303d0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CParentDefClassBase_operator_00431ed0.cpp clean inline_asm +00/43/CParentDefClassBase_SetInstantiationName_004303c0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CPlayerGui_Close_00438400.cpp clean inline_asm +00/43/CPlayerGui_DrawScreenFade_00437670.cpp clean inline_asm +00/43/CPlayerGui_ForceClose_00438440.cpp clean inline_asm +00/43/CPlayerGui_ForceOpen_004374d0.cpp clean inline_asm +00/43/CPlayerGui_GetPHeroInfoScreensManager_00437cd0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CPlayerGui_OnHeroHit_00437950.cpp clean inline_asm +00/43/CPlayerGui_Open_004374b0.cpp clean inline_asm +00/43/CPlayerGui_PeekGuiDef_00438570.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CPlayerGui_SetButtonAlpha_004382e0.cpp clean inline_asm +00/43/CPlayerGui_SetSpecialAbilityButtonsAsDisplaying_00437580.cpp clean inline_asm +00/43/CPlayerGui_SetVWindowForDeprecatedDrawInterface_004375f0.cpp clean inline_asm +00/43/CStartupDefinitionManager_CStartupDefinitionManager_00433693.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CStartupDefinitionManager_ErrorCallback_0043368c.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CStartupDefinitionManager_Get_0043368d.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CStartupDefinitionManager_InitAndCompile_0043368b.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CStartupDefinitionManager_InitAndCompilePlatformSpecific_004336bc.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CStartupDefinitionManager_Set_004336b0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/CSubtitleRenderer_GetTextWidthFromText_004372a0.cpp clean inline_asm +00/43/CTCHeroStats_SoberUp_00436560.cpp clean inline_asm +00/43/CTCInventoryItem_OnDie_00430300.cpp clean inline_asm +00/43/CThingBuildingDef_operator_00431f10.cpp clean inline_asm +00/43/CThingTrackNode_vectordeletingdestructor_00431e40.cpp clean inline_asm +00/43/CTopologySizeGrid_Hora_00436830.cpp clean ptr_warning C4312 +00/43/CVectorMapThingComponents_FindConst_00436860.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/43/CVectorMapThingComponents_LowerBoundConst_004365b0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/43/CWorldMap_PrepareForLoad_004382c0.cpp clean inline_asm +00/43/DestvalstdallocatorstdpairlongCMusicManagerCRegisteredMusicEntrystdpairlongCMusicManager_CRegisteredMusicEntry_004364c0.cpp clean inline_asm +00/43/EnginePointerList_Destructor_00436900.cpp clean ptr_warning C4302,C4311,C4312 +00/43/global_Alloc_00431773.cpp clean ptr_warning C4302,C4311,C4312 +00/43/global_Fillnunsignedlongunsignedintunsigned_00433504.cpp clean ptr_warning C4302,C4311,C4312 +00/43/global_GFSerialiseVectorBinaryInCCharString_00433273.cpp clean ptr_warning C4302,C4311,C4312 +00/43/global_GFSerialiseVectorBinaryOutCCharStrin_00433227.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/43/global_VectorEraseAndMove_00436a80.cpp clean ptr_warning C4302,C4311,C4312 +00/43/PairVector_Clear_0043211d.cpp clean ptr_warning C4302,C4311,C4312 +00/43/STL_DefStringMapErase_0043020a.cpp clean ptr_warning C4302,C4311,C4312 +00/43/STL_insertunique_00431606.cpp clean ptr_warning C4302,C4311,C4312 +00/43/STL_Minsert_004316cf.cpp clean ptr_warning C4302,C4311,C4312 +00/43/STL_operator_00433df0.cpp clean ptr_warning C4302,C4311,C4312 +00/43/STLStringVector_erase_0043336a.cpp clean ptr_warning C4302,C4311,C4312 +00/43/STLStringVector_Mfillinsert_004333b3.cpp clean ptr_warning C4302,C4311,C4312 +00/43/STLStringVector_Minsertoverflow_00433530.cpp clean ptr_warning C4302,C4311,C4312 +00/43/UninitcopyCArrayCRandomAppearanceMorphCBodyPartMeshCArrayclassCRandomAppearanceMorphCBodyPartMeshstdallocatorCArrayCRandomAppearanceMorph_CBodyPartMesh_00433d8a.cpp clean inline_asm +00/43/Uninitfillnstdpairunsignedint64CCountedPointerCActiveEntityScriptBaseunsignedint_pairunsignedint64classCCountedPointerclassCActiv_0043f6c0.cpp clean inline_asm +00/43/vectorbool_operator_004372c0.cpp clean inline_asm +00/44/CAIStateGroupIdleAtHome_InitCreaturePosition_004496b0.cpp clean inline_asm +00/44/CCombatWheel_ResetRings_00442870.cpp clean inline_asm +00/44/CDefString_TransferBinaryIn_0044fc00.cpp clean ptr_warning C4302,C4311,C4312 +00/44/CEngineScreenEffectDisplacementRenderer_FinishWithDynamicTextures_00443140.cpp clean inline_asm +00/44/CEngineScreenEffectOutlineGlow_FinishWithDynamicTextures_00441380.cpp clean inline_asm +00/44/CEngineSubPrimitiveText_scalardeletingdestructor_00443c50.cpp clean inline_asm +00/44/CEngineSubPrimitiveText_scalardeletingdestructor_00443ca0.cpp clean inline_asm +00/44/CGameDefinitionManager_Get_0044c6b0.cpp clean ptr_warning C4302,C4311,C4312 +00/44/CGameDefinitionManager_IsAllocated_0044c6b6.cpp clean ptr_warning C4302,C4311,C4312 +00/44/CGameDefinitionManager_Set_0044c71f.cpp clean ptr_warning C4302,C4311,C4312 +00/44/CGamePlayerInterface_GetAssignableSpecialMoveIndexForButton_00445ee0.cpp clean inline_asm +00/44/CGamePlayerInterface_GetAssignableSpecialMoveIndexForButton_00445f40.cpp clean inline_asm +00/44/CGamePlayerInterface_GetAssignableSpecialMoveIndexForButton_00445fa0.cpp clean inline_asm +00/44/CGamePlayerInterface_IsAssignableSpecialMoveButtonInQueue_00445e90.cpp clean inline_asm +00/44/CGamePlayerInterface_IsAssignableSpecialMoveButtonInQueue_004460c0.cpp clean inline_asm +00/44/CGamePlayerInterface_IsAssignableSpecialMoveButtonInQueue_004461d0.cpp clean inline_asm +00/44/CGamePlayerInterface_IsEventAssignableSpecialMoveButton_00445e30.cpp clean inline_asm +00/44/CGamePlayerInterface_IsEventContextSensitiveItemButton_00446000.cpp clean inline_asm +00/44/CGamePlayerInterface_IsEventGameAction_00445bd0.cpp clean inline_asm +00/44/CGamePlayerInterface_IsGameActionEventInQueue_00446b10.cpp clean inline_asm +00/44/CGamePlayerInterface_IsOtherQuickAccessItemButtonHeld_00446060.cpp clean inline_asm +00/44/CGamePlayerInterface_LoadInitialInfo_00445b40.cpp clean inline_asm +00/44/CGamePlayerInterface_SaveInitialInfo_00445ae0.cpp clean inline_asm +00/44/CGuiControlTreePane_SortTreeRecursively_00440730.cpp clean inline_asm +00/44/CGuiControlTreePane_SortTreeRecursively_00440770.cpp clean inline_asm +00/44/CInputTypeXboxPadButtonEvent_IsInputTypeWithButton_00445630.cpp clean inline_asm +00/44/CItem_operator_00448380.cpp clean inline_asm +00/44/CLandscapeBackgroundPatch_vectordeletingdestructor_0044fcd7.cpp clean inline_asm +00/44/CLandscapeBackgroundPatch_vectordeletingdestructor_0044fd16.cpp clean inline_asm +00/44/CLandscapeBackgroundPatch_vectordeletingdestructor_0044fd55.cpp clean inline_asm +00/44/CLandscapeBackgroundPatch_vectordeletingdestructor_0044fd94.cpp clean inline_asm +00/44/CLandscapeBackgroundPatch_vectordeletingdestructor_0044fdd3.cpp clean inline_asm +00/44/CLandscapeBackgroundPatch_vectordeletingdestructor_0044fe12.cpp clean inline_asm +00/44/CMiniMapDisplay_ClearPrimitiveHandles_00443cf0.cpp clean ptr_warning C4302,C4311 +00/44/ConsvalstdallocatorstdpairCWideStringconstfloatstdpairCWideStringconstfloatstd_Consval_0044b110.cpp clean inline_asm +00/44/CPatchTesselationEdgeStrip_CPatchTesselationEdgeStrip_0044a140.cpp clean inline_asm +00/44/CPlayerManager_GetMainPlayer_00449970.cpp clean ptr_warning C4302,C4311,C4312 +00/44/CPlayerManager_GetMainPlayerNumber_00449700.cpp clean ptr_warning C4302,C4311,C4312 +00/44/CPlayerManager_GetPlayer_004498c0.cpp clean ptr_warning C4302,C4311,C4312 +00/44/CPlayerManager_GetPlayerNumberFromJoystickDeviceNumber_00449990.cpp clean inline_asm +00/44/CPlayerManager_IsPlayer_00449880.cpp clean inline_asm +00/44/CPlayerManager_IsPlayerAssociatedWithJoystickDeviceNumber_004499e0.cpp clean inline_asm +00/44/CPlayerManager_IsThingMainPlayer_004497c0.cpp clean inline_asm +00/44/CPlayerManager_PeekMainPlayer_00449960.cpp clean ptr_warning C4302,C4311,C4312 +00/44/CPlayerManager_PeekPlayer_00449910.cpp clean ptr_warning C4302,C4311,C4312 +00/44/CShaderPreParser_GetConstantCount_00445940.cpp clean inline_asm +00/44/CShaderPreParser_GetConstantCount_00445950.cpp clean inline_asm +00/44/CSpellContainerList_AddChild_00449b20.cpp clean inline_asm +00/44/CTCAssassinRush_FinishMove_00443190.cpp clean inline_asm +00/44/CTCAssassinRush_FinishMove_004431c0.cpp clean inline_asm +00/44/CTCAssassinRush_FinishMove_004431f0.cpp clean inline_asm +00/44/CTCHeroExperience_UpdateAbilitiesForAllStats_0044a530.cpp clean inline_asm +00/44/CTCShotDefinitionBase_CanBeHitByMeleeStrike_00445ba0.cpp clean inline_asm +00/44/CTCShotDefinitionBase_CanBeHitByMeleeStrike_00445bb0.cpp clean inline_asm +00/44/CTCShotDefinitionBase_CanBeHitByMeleeStrike_00445bc0.cpp clean inline_asm +00/44/CVectorMaplong_LowerBound_00447c80.cpp clean inline_asm +00/44/CWound_Kill_004425c0.cpp clean inline_asm +00/44/Destroyrangestd_allocatorCSpeechFrame_00440ba0.cpp clean inline_asm +00/44/Destroyrangestd_allocatorCSpeechFrame_00440bd0.cpp clean inline_asm +00/44/Destroyrangestdallocatorstd_pairCCharStringCCountedPointerCEntityScriptBindi_00448860.cpp clean inline_asm +00/44/Destroyrangestdallocatorstd_pairlongCCountedPointerCRegionConnectionNode_004488b0.cpp clean inline_asm +00/44/global_BufferMoveData_00440550.cpp clean ptr_warning C4302,C4311,C4312 +00/44/global_BufferRelocate_00440590.cpp clean ptr_warning C4302,C4311,C4312 +00/44/global_CopyimplCNavigationPositionCNavigationPosition_00448910.cpp clean inline_asm +00/44/global_Fillnunsignedlongunsignedintunsignedlong_0044b150.cpp clean inline_asm +00/44/UninitcopyCFeatDefCFeatDefstd_allocatorCFeatDef_0044b120.cpp clean inline_asm +00/44/UninitcopystdVectorconstiteratorstdVectorvalCScriptThingstdallocatorCScriptThing_allocatorCScriptThing_004400c0.cpp clean inline_asm +00/44/Uninitfillnstdpairunsignedint64CCountedPointerCActiveEntityScriptBaseunsignedint_pairunsignedint64classCCountedPointerclassCActiv_004482c0.cpp clean inline_asm +00/44/vectorunsignedint_resize_00442770.cpp clean inline_asm +00/45/CEngineLightingManager_UpdateShadowScene_00451efd.cpp clean ptr_warning C4302,C4311 +00/45/CEngineLightingManager_UpdateShadowScene_004521b2.cpp clean ptr_warning C4302,C4311 +00/45/CEngineLightingManager_UpdateShadowScene_00452a60.cpp clean ptr_warning C4302,C4311 +00/45/CTCCarryable_OnKill_00450edf.cpp clean ptr_warning C4312 +00/45/DestvalstdallocatorstdpairEAnimComponentCIVCountedPointerCAnimComponentBasestd_pairEAnimComponentCIVCountedPointerCAnimComponen_00450bba.cpp clean inline_asm +00/46/CStringParser_CopyConstructor_0046381b.cpp clean ptr_warning C4302,C4311,C4312 +00/46/CVsyncCallbackTimerInfo_CVsyncCallbackTimerInfo_00469435.cpp clean inline_asm +00/47/CArray_push_back_0047662a.cpp clean inline_asm +00/47/CArray_push_back_004799c6.cpp clean inline_asm +00/47/CTCInGameMenu_PeekGuiDef_0047d5a3.cpp clean inline_asm +00/47/CUserProfileManager_ValidateSaveFile_0047ed0a.cpp clean inline_asm +00/47/global_Fillnunsignedlongunsignedintunsignedlong_0047b6c0.cpp clean inline_asm +00/47/global_Uninitcopyuinta_0047b5f0.cpp clean inline_asm +00/47/global_Uninitcopyuintc_0047c290.cpp clean inline_asm +00/47/global_Uninitcopyuintd_0047b670.cpp clean inline_asm +00/47/global_Uninitcopyuinte_0047b710.cpp clean inline_asm +00/47/STLStringLongTree_Mlowerbound_0047547d.cpp clean ptr_warning C4302,C4311,C4312 +00/47/Uninit_copy_uint_047b670.cpp clean inline_asm +00/47/UninitcopystdVectorconstiteratorstdVectorvalunsignedintstdallocatorunsignedintun_allocatorunsignedint_0047b830.cpp clean inline_asm +00/48/ByteVector_InsertFill_00485f3a.cpp clean inline_asm +00/48/CMainGameComponent_BeginInputLoading_0048faa0.cpp clean inline_asm +00/48/CPersistContext_IsBinary_0048db13.cpp clean inline_asm +00/48/CPersistContextReadSection_CPersistContextReadSection_00485cd2.cpp clean inline_asm +00/48/CPlayer_GetPControlledCreature_00487dc0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/48/CPlayer_IsMainPlayer_00487520.cpp clean ptr_warning C4302,C4311,C4312 +00/48/CPlayer_IsTargetLocked_004874a0.cpp clean inline_asm +00/48/CPlayer_PeekPControlledCreature_00487dd0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/48/CPlayer_ToggleAggressiveMode_00487e90.cpp clean ptr_warning C4302,C4311,C4312 +00/48/CPlayer_UnfreezeControls_00487120.cpp clean ptr_warning C4302,C4311,C4312 +00/48/global_Fillnunsignedlongunsignedintunsignedlong_0048514c.cpp clean inline_asm +00/49/CAProgressDisplay_InitialiseTextures_00497850.cpp clean ptr_warning C4302,C4311,C4312 +00/49/CAProgressDisplay_SetToDisplayText_00497860.cpp clean ptr_warning C4302,C4311,C4312 +00/49/CFrameRateSmoother_AddFrameTime_0049b9e0.cpp clean ptr_warning C4302,C4311,C4312 +00/49/CFrameRateSmoother_CalculateSmoothedTime_0049b9a0.cpp clean ptr_warning C4302,C4311,C4312 +00/49/CFrameRateSmoother_GetSmoothedTime_0049b990.cpp clean ptr_warning C4302,C4311,C4312 +00/49/CFrameRateSmoother_Init_0049ba70.cpp clean ptr_warning C4302,C4311,C4312 +00/49/CFrameRateSmoother_Uninitialise_0049ba20.cpp clean ptr_warning C4302,C4311,C4312 +00/49/CFrontendGameComponent_ChooseProfileNumber_004962c0.cpp clean inline_asm +00/49/CFrontendGameComponent_ChooseSaveNumber_00494800.cpp clean inline_asm +00/49/CFrontendGameComponent_ChooseWorldNumber_00494710.cpp clean inline_asm +00/49/CFrontendGameComponent_ClearProfileResources_00495780.cpp clean inline_asm +00/49/CFrontendGameComponent_CreateNewProfile_004945b0.cpp clean inline_asm +00/49/CFrontendGameComponent_Destroy_00495620.cpp clean inline_asm +00/49/CFrontendGameComponent_GetMaxVisibleProfileNumber_00494520.cpp clean inline_asm +00/49/CFrontendGameComponent_GetMaxVisibleProfileNumber_00494680.cpp clean inline_asm +00/49/CFrontendGameComponent_GetMaxVisibleProfileNumber_00494770.cpp clean inline_asm +00/49/CFrontendGameComponent_InitGraphics_00494840.cpp clean inline_asm +00/49/CFrontendGameComponent_PopulateProfiles_00495890.cpp clean inline_asm +00/49/CFrontendGameComponent_ShowMoreProfiles_00494560.cpp clean inline_asm +00/49/CFrontendGameComponent_ShowMoreProfiles_004946c0.cpp clean inline_asm +00/49/CFrontendGameComponent_ShowMoreProfiles_004947b0.cpp clean inline_asm +00/49/CFrontendProfileList_Refresh_00495830.cpp clean inline_asm +00/49/CGameJoystickManager_GetCGameJoystickManager_0049bb90.cpp clean ptr_warning C4302,C4311,C4312 +00/49/CGameJoystickManager_GetMainPlayerJoystickDeviceNumber_0049bb40.cpp clean ptr_warning C4302,C4311,C4312 +00/49/CGameJoystickManager_HasExplicitMainPlayerJoystickDeviceNumber_0049bb50.cpp clean ptr_warning C4302,C4311,C4312 +00/49/CParticleEnvironmentInteraction_GetCameraPos_0049bdc0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CProgressDisplay_Constructor_00499ce0.cpp clean error C2821 fable_boot.h:82 C2821 first formal parameter to 'operator new' must be 'size_t' +00/49/CProgressDisplay_FormatTextForVWindow_00497f80.cpp clean ptr_warning C4302,C4311,C4312 +00/49/CProgressDisplay_GetPTextBank_00497b30.cpp clean error C2821 fable_boot.h:82 C2821 first formal parameter to 'operator new' must be 'size_t' +00/49/CProgressDisplay_IsActive_0049b460.cpp clean error C2821 fable_boot.h:82 C2821 first formal parameter to 'operator new' must be 'size_t' +00/49/CProgressDisplay_SetToDisplayText_00499a70.cpp clean error C2821 fable_boot.h:82 C2821 first formal parameter to 'operator new' must be 'size_t' +00/49/CThingSearchTools_CThingSearchTools_0049c7a0.cpp clean inline_asm +00/49/CWorld_ActivateWorld_0049f180.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_ChangeResolution_0049d820.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_ChangeTextureColourDepth_0049d830.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_ConvertWorldToISMThingyPath_0049d6b0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_ConvertWorldToISMThingyPath_0049dec0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_ConvertWorldToStaticMapPath_0049d550.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_ConvertWorldToStaticMapPath_0049ddd0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_DeleteCheckPointSave_0049e740.cpp layout fable_world.h error C2821 fable_world.h:125 C2118 negative subscript +00/49/CWorld_DrawGetEnvironment_0049e350.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_DrawGetWorldMap_0049e1a0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_EventIsWorldEvent_0049d8c0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_GetChecksum_0049e200.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_GetFrame_0049d870.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_GetPlayer_0049d850.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_GetThingManager_0049e1b0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_IsHeroStateFileValid_0049d4e0.cpp clean inline_asm +00/49/CWorld_PeekThingManager_0049e1c0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_PostLoadInit_0049d970.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_ProcessEvent_0049e1d0.cpp clean inline_asm +00/49/CWorld_SetAsPaused_0049d8f0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/CWorld_UpdateInputRelatedInterpolation_0049d9e0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/49/global_GFROR13_00497890.cpp clean inline_asm +00/4a/CNetworkClient_GetLocalGameEventPackageSet_004aeaa0.cpp clean ptr_warning C4302,C4311,C4312 +00/4a/CNetworkClient_Uninitialise_004ae990.cpp clean ptr_warning C4302,C4311,C4312 +00/4a/CNetworkClient_Update_004ae9d0.cpp clean ptr_warning C4302,C4311,C4312 +00/4a/CNetworkClient_WaitForSync_004ae9c0.cpp clean ptr_warning C4302,C4311,C4312 +00/4a/CQuestManager_SetObjectiveAsCompleted_004af960.cpp clean inline_asm +00/4a/CScriptThing_DecrementScriptCounter_004ab100.cpp clean inline_asm +00/4a/CScriptThing_IncrementScriptCounter_004ab0f0.cpp clean inline_asm +00/4a/CScriptThing_SetActivationTriggerStatus_004ab0a0.cpp clean inline_asm +00/4a/CScriptThing_SetFriendsWithEverythingFlag_004ab060.cpp clean inline_asm +00/4a/CScriptThing_UpdateThingAttachment_004ab0e0.cpp clean inline_asm +00/4a/CSymbolMap_ContainerLifetime_004ad4a0.cpp error C2995 error C2995 CSymbolMap_ContainerLifetime_004ad4a0.cpp:12 C2995 'CVectorMap>::~CVectorMap(void)': function t +00/4a/CTCTrapBase_ManualReset_004ae9a0.cpp clean inline_asm +00/4a/CWorld_DrawGetProceduralTexture_004ae890.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/4a/CWorld_GetWorldMap_004ae8b0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/4a/CWorld_LoadHeroStateInternal_004a2d70.cpp clean inline_asm +00/4a/CWorld_PeekWorldMap_004ae8c0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/4a/CWorld_PrepareForLoad_004a0220.cpp clean inline_asm +00/4a/InstallerCompletedFileSet_Erase_004aba70.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/4a/STLSetString_InsertNode_004abc70.cpp clean ptr_warning C4302,C4311,C4312 +00/4a/STLSetString_InsertUnique_004ac380.cpp clean ptr_warning C4302,C4311,C4312 +00/4b/CMainGameComponent_InitLUA_004bbc00.cpp clean ptr_warning C4302,C4311,C4312 +00/4b/ConsvalstdallocatorstdpairCCharStringconststdpairlonglongstdpairCCharStringconst_pairlonglong_004b8330.cpp clean error C2821 ConsvalstdallocatorstdpairCCharStringconststdpairlonglongstdpairCCharStringconst_pairlonglong_004b8330.cpp:5 C2821 first formal parameter to 'operator new' must be 'size_t' +00/4b/ConsvalstdallocatorstdpairCWideStringconstfloatstdpairCWideStringconstfloatstd_pairCWideStringfloat_004b82d0.cpp clean error C2821 ConsvalstdallocatorstdpairCWideStringconstfloatstdpairCWideStringconstfloatstd_pairCWideStringfloat_004b82d0.cpp:4 C2821 first formal parameter to 'operator new' must be 'size_t' +00/4b/CQuestManager_DeactivateAllQuests_004b4450.cpp clean inline_asm +00/4b/CTCGraphicAppearance_GetAnims_004bc000.cpp clean inline_asm +00/4c/CBulletTimeManager_ConvertInterpolateToBulletTimeInterpolate_004c6010.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/4c/CPhysicalPrimitiveMesh_GetClosestPointToPos2D_004cf3d0.cpp clean inline_asm +00/4c/CPhysicalPrimitiveMesh_GetClosestPointToPos2D_004cfbc0.cpp clean inline_asm +00/4c/CRenderTarget_DetachTarget_004c7590.cpp clean inline_asm +00/4c/CThing_IsGameStatePersisted_004c8280.cpp clean inline_asm +00/4c/CThing_PeekDefinitionManager_004c7990.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/4c/CThing_SetCurrentThingManager_004c74f0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/4d/CActionDoCreatureAction_GetActionName_004d4249.cpp clean inline_asm +00/4d/CActionDoCreatureAction_GetActionName_004d429b.cpp clean inline_asm +00/4d/CActionDoCreatureAction_GetActionName_004d430f.cpp clean inline_asm +00/4d/CActionDoCreatureAction_GetActionName_004d5fdb.cpp clean inline_asm +00/4e/CSoundInit_CSoundInit_004ec9b5.cpp clean inline_asm +00/4f/CWorldMap_DrawFindLastUnblockedGroundPositionAndReportColl_004fb290.cpp clean inline_asm +00/4f/CWorldMap_DrawFindLastUnblockedThemePositionAndReportColli_004fb2a0.cpp clean inline_asm +00/4f/CWorldMap_GetLoadedRegion_004fb150.cpp clean ptr_warning C4302,C4311,C4312 +00/4f/CWorldMap_GetRegion_004fc180.cpp clean ptr_warning C4302,C4311,C4312 +00/4f/CWorldMap_IsPosChangeable_004fb2f0.cpp clean inline_asm +00/4f/CWorldMap_RegisterScriptedMapBrush_004ff900.cpp clean ptr_warning C4312 +00/50/CBankFile_NotifyPostEntriesLoaded_0050b440.cpp clean ptr_warning C4302,C4311,C4312 +00/51/CGameComponent_CGameComponent_0051d8e0.cpp clean ptr_warning C4302,C4311,C4312 +00/51/ConsvalstdallocatorstdpairCCharStringconststdpairlonglongstdpairCCharStringconst_pairlonglong_00511f30.cpp clean error C2821 ConsvalstdallocatorstdpairCCharStringconststdpairlonglongstdpairCCharStringconst_pairlonglong_00511f30.cpp:13 C2821 first formal parameter to 'operator new' must be 'size_t' +00/51/CThingManager_GetNonLocalThingChecksum_0051e530.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/51/STLMapCCharStringlong_insert_unique_hint_00516af0.cpp clean ptr_warning C4302,C4311,C4312 +00/51/STLMapCCharStringlong_operator_00519230.cpp clean ptr_warning C4302,C4311,C4312 +00/52/CFrontEndManager_Initialize_0052d9e0.cpp clean ptr_warning C4302,C4311,C4312 +00/52/CObserver_ObserveEvent_0052da20.cpp clean inline_asm +00/53/CComponent_Die_00530720.cpp clean inline_asm +00/53/CList_DoRecomputeOffsets_0053c332.cpp clean inline_asm +00/53/CList_InitialiseOffsets_00536b4f.cpp clean inline_asm +00/53/CList_ProcessEvent_0053673b.cpp clean inline_asm +00/53/CList_SetSelectedChild_005360b1.cpp clean inline_asm +00/54/CFrontEndList_InitialiseOffsets_0054c480.cpp clean inline_asm +00/54/CFrontEndList_ScrollDown_0054c810.cpp clean inline_asm +00/54/CFrontEndList_ScrollUp_0054c4c0.cpp clean inline_asm +00/54/CFrontEndScreen_CFrontEndScreen_0054e3d0.cpp clean inline_asm +00/54/CFrontEndScreen_dtor_0054e450.cpp clean inline_asm +00/54/CFrontEndScreen_Initialise_0054e4b0.cpp clean inline_asm +00/54/CMovie_FreeTextures_00547b90.cpp clean inline_asm +00/54/CMovie_IsPlaying_00548520.cpp clean ptr_warning C4302,C4311,C4312 +00/54/CMovie_SetMovie_00548510.cpp clean ptr_warning C4302,C4311,C4312 +00/55/CHoverable_OnUnhovered_0055b9a0.cpp clean inline_asm +00/55/CMorphingSprite_InternalChanged_00554110.cpp clean inline_asm +00/55/CTable_GetDataFromDef_00551340.cpp clean inline_asm +00/55/CTable_Initialise_00550c60.cpp clean inline_asm +00/56/CTCInventoryExperience_GetOriginalExperience_0056ea9a.cpp error C2039 error C2039 CTCInventoryExperience_GetOriginalExperience_0056ea9a.cpp:5 C2039 'GetOriginalExperience': is not a member of 'CTCInventoryExperience' +00/56/CTCInventoryExperience_IncrementStatIndicator_0056e5ca.cpp clean inline_asm +00/57/CDateAndTime_CDateAndTime_0057a376.cpp clean inline_asm +00/57/CRegion_SetMiniMapRegionExitTextOffsetX_00578e02.cpp clean ptr_warning C4312 +00/57/CTattooDef_GetTypeBalancedAttractiveness_0057fc46.cpp clean inline_asm +00/57/CTCHeroStats_HasCurrentMarriage_0057ac4b.cpp error C2039 error C2039 CTCHeroStats_HasCurrentMarriage_0057ac4b.cpp:4 C2039 'HasCurrentMarriage': is not a member of 'CTCHeroStats' +00/57/CTCHeroStats_InformOfEvent_0057a56c.cpp clean inline_asm +00/57/CTCHeroStats_UpgradeRenownLevel_0057cae9.cpp clean inline_asm +00/57/global_UIntRBTreeFindLess_0057fed6.cpp clean inline_asm +00/57/STLBankStringTree_erase_00579435.cpp clean ptr_warning C4302,C4311,C4312 +00/58/CTCInventory_ConstructItemDescription_005819dd.cpp clean inline_asm +00/59/CCreatureActionFireMissileWeapon_FrameUpdate_0059b61c.cpp clean inline_asm +00/59/CFrontEndManager_Action_0059a238.cpp clean inline_asm +00/59/CFrontEndManager_Action_0059a238_match.cpp clean ptr_warning C4302,C4311,C4312 +00/59/CFrontEndManager_AddPrefixToTitleBar_00595ad9.cpp clean inline_asm +00/59/CFrontEndManager_AddProfileNameToScreens_00595b24.cpp clean inline_asm +00/59/CFrontEndManager_CanAcceptOptionChanges_005966f2.cpp clean inline_asm +00/59/CFrontEndManager_CanStartAttractMode_00595a03.cpp clean ptr_warning C4302,C4311,C4312 +00/59/CFrontEndManager_CFrontEndManager_005953e2.cpp clean ptr_warning C4302,C4311,C4312 +00/59/CFrontEndManager_ChangeStateFirstTime_005952c3.cpp clean ptr_warning C4302,C4311,C4312 +00/59/CFrontEndManager_CreateFrontEndButtonComponent_00596a68.cpp clean inline_asm +00/59/CFrontEndManager_CreateNewProfile_0059697a.cpp clean inline_asm +00/59/CFrontEndManager_DeactivateAllEditBoxes_005952d8.cpp clean inline_asm +00/59/CFrontEndManager_DoPressStart_00599d5c.cpp clean inline_asm +00/59/CFrontEndManager_Draw_00595222.cpp clean ptr_warning C4302,C4311,C4312 +00/59/CFrontEndManager_GetInstance_00595582.cpp clean ptr_warning C4302,C4311,C4312 +00/59/CFrontEndManager_GetProfileNames_005955ab.cpp clean inline_asm +00/59/CFrontEndManager_GetScoreboardEditBox_00596379.cpp clean inline_asm +00/59/CFrontEndManager_GetScoreboardOptionsToProfile_00596550.cpp clean inline_asm +00/59/CFrontEndManager_GotoNewProfileScreen_00595845.cpp clean inline_asm +00/59/CFrontEndManager_GotoNextScreen_00596763.cpp clean inline_asm +00/59/CFrontEndManager_GotoPreviousScreen_00597bf2.cpp clean inline_asm +00/59/CFrontEndManager_GotoProfileMenu_00597b20.cpp clean inline_asm +00/59/CFrontEndManager_GotoSpecificPreviousScreen_0059686d.cpp clean inline_asm +00/59/CFrontEndManager_Init_005958f5.cpp clean inline_asm +00/59/CFrontEndManager_LaunchVirtualKeyboard_00596917.cpp clean inline_asm +00/59/CFrontEndManager_LoadProfile_0059899a.cpp clean inline_asm +00/59/CFrontEndManager_RefreshAvailableProfiles_00596cce.cpp clean inline_asm +00/59/CFrontEndManager_RefreshAvailableProfilesForDelete_00597006.cpp clean inline_asm +00/59/CFrontEndManager_ResetSlidersToProfileValues_00595356.cpp clean inline_asm +00/59/CFrontEndManager_SetMainMenuDefName_00595a06.cpp clean inline_asm +00/59/CFrontEndManager_SetScoreboardEditBox_0059630e.cpp clean inline_asm +00/59/CFrontEndManager_SetScoreboardOptionsFromProfile_005963db.cpp clean inline_asm +00/59/CGameEvent_AddUBYTE_0059b310.cpp clean ptr_warning C4302,C4311,C4312 +00/59/CTCInventoryExperience_IncrementStatIndicator_0059aaf9.cpp clean inline_asm +00/59/CTCInventoryTrade_ProcessButtonAReleased_0059b777.cpp clean inline_asm +00/59/CUserProfileManager_VerifyAndLoadProfile_005957d9.cpp clean inline_asm +00/59/FrontEnd_FrameUpdate_0059672a.cpp clean inline_asm +00/59/FrontEndDequeIterator_Distance_0059a8f3.cpp clean inline_asm +00/59/FrontEndHistoryDeque_AllocateBlocks_0059aa43.cpp clean ptr_warning C4302,C4311,C4312 +00/59/FrontEndHistoryDeque_FreeBlocks_0059aa64.cpp clean inline_asm +00/59/FrontEndHistoryDequeIterator_DistanceThunk_0059aae3.cpp clean inline_asm +00/59/STLScreenDequeBase_Dequebase_0059aef9.cpp clean ptr_warning C4302,C4311,C4312 +00/59/STLScreenDequeBase_Minitializemap_0059ac9f.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/5b/CCategory_GetCategoryIdentifier_005bd1b3.cpp clean inline_asm +00/5b/CItem_IsQuestPersistent_005bc729.cpp clean inline_asm +00/5b/CItem_UpdateNonQuestPersistentRefCount_005bc70a.cpp clean inline_asm +00/5b/CRenderTarget_DetachTarget_005bd993.cpp clean inline_asm +00/5b/CTCInventoryBase_GetCategoryWithIndex_005bce3c.cpp clean inline_asm +00/5b/CTCInventoryBase_GetSelectedCategory_005bcace.cpp clean inline_asm +00/5b/CTCInventoryBase_ProcessButtonAReleased_005bc638.cpp clean inline_asm +00/5b/CTCInventoryBase_RemoveItemFromInventory_005bc499.cpp clean inline_asm +00/5b/CTCWeapon_GetAnimationSpeedValue_005bd404.cpp clean inline_asm +00/5c/CNavigationLayer_CNavigationLayer_005c14a6.cpp clean inline_asm +00/5c/Ucopystd_pairlongCCountedPointerCContainmentVolume_005c13cd.cpp clean inline_asm +00/5c/Ucopystd_pairlongCCountedPointerCContainmentVolume_005c13e3.cpp clean inline_asm +00/5c/Ucopystd_pairlongCCountedPointerCContainmentVolume_005c190b.cpp clean inline_asm +00/5c/Ucopystd_pairlongCCountedPointerCContainmentVolume_005c1921.cpp clean inline_asm +00/5d/CCategory_GetCategoryIdentifier_005d9030.cpp clean inline_asm +00/5d/CCategory_GetCategoryIdentifier_005da7d0.cpp clean inline_asm +00/5d/CChunkCollectionIndicator_OnPredicateFail_005d8e50.cpp clean inline_asm +00/5d/CPersistTraitsEInputKey_TransferIn_005d8167.cpp clean ptr_warning C4302,C4311 +00/5d/CTCWeapon_IsAbleToBeUsedToBlock_005dacf0.cpp clean inline_asm +00/5d/CTCWeapon_IsAnalogueLoading_005da8f0.cpp clean inline_asm +00/5d/CTCWeapon_IsMeleeWeapon_005daa70.cpp clean inline_asm +00/5d/CTCWeapon_IsProjectileWeapon_005daa40.cpp clean inline_asm +00/5d/global_IsMeleeWeaponClass_005da510.cpp clean inline_asm +00/5e/CTCInventoryAbilities_IsAbilityAvailable_005edf08.cpp clean inline_asm +00/5e/CTCTavernGame_IsCurrentOrUpcomingState_005e9bf0.cpp clean inline_asm +00/5e/CTCTavernGame_SetStateAfterYesNo_005eaf30.cpp clean inline_asm +00/5e/CTCTavernGame_SetStateAfterYesNoAlt_005eadc0.cpp clean inline_asm +00/62/CWorld_EAApplyAllScriptedMapBrushes_00629210.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EACentreCamera_006295a0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAClickPastText_00629f30.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EACloseHeroInformationScreen_0062b3f0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EACloseInGameMenu_00629d60.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EACloseLiveGUI_00629da0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAClosePhotoCapture_0062b4d0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAConsoleToggleState_0062a110.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAControlledCreatureBlockEnd_0062b000.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAControlledCreatureDeactivateLightning_00629530.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAControlledCreatureDeactivateZTarget_00629240.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAControlledCreatureDefault_006298f0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAControlledCreatureStartThrowObject_00629220.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAControlledCreatureUnfreezeControls_006291f0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EACreatureCheat_006292a0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EADeleteMenuComponents_0062c980.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EADeleteTradeActiveMenu_0062c9d0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAFirstPersonTargeting_00629510.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAFirstPersonViewStart_00629280.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAMoveHeroToRegion_00629260.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAOpenPCBuyTradingMenu_0062c7f0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAOpenPCInventoryMenu_0062c700.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAOpenPCMapMenu_0062c8e0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAOpenPCMSNChatMenu_0062c6e0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAOpenPCOptionsMenu_0062c750.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAOpenPCPhotoJournalMenu_0062c7a0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAOpenPCScoreboard_0062c640.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAOpenPCSellTradingMenu_0062c840.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAOpenPCSkillsMenu_0062c690.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAOpenPCStatusMenu_0062c930.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAOpenPCWantedTradingMenu_0062c890.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAPlayerRespawn_006294a0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EARemoveCurrentMode_0062c4b0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EASetFreeCameraMode_00629390.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EASetPauseMode_006292d0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EASetSlowMotion_00629330.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAToggleKillEverythingMode_00629570.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EATogglePassiveAggressiveMode_00629550.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAUpdateFrame_00629270.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/62/CWorld_EAUseFreeCamera_00629420.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/63/CPlayer_GetCurrentMode_00633be0.cpp clean ptr_warning C4302,C4311,C4312 +00/63/CPlayer_HasMode_00634160.cpp clean ptr_warning C4302,C4311,C4312 +00/64/CDrawMiniMap_ClearPrimitivesIfNecessary_00643de5.cpp error C2039 error C2039 CDrawMiniMap_ClearPrimitivesIfNecessary_00643de5.cpp:14 C2039 'sub': is not a member of 'CDrawMiniMap' +00/64/CEngineManager_List_006404d0.cpp clean ptr_warning C4302,C4311,C4312 +00/64/EnginePointerList_Remove_006405b0.cpp clean ptr_warning C4302,C4311,C4312 +00/65/global_Fill_00654e46.cpp clean inline_asm +00/65/global_Fillnunsignedlongunsignedintunsignedlong_00654d0c.cpp clean inline_asm +00/66/C2DVector_DivideByScalar_0066f990.cpp clean inline_asm +00/66/CCharString_ConstructCCoopSpiritDef_0066fa10.cpp clean inline_asm +00/66/CDefinitionManager_GetDefCombatTypeIndex_00669640.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/66/CDefinitionManager_GetDefCombatTypeName_00669770.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/66/CFrontEndManager_CreateComponent_00661d20.cpp clean inline_asm +00/66/CFrontEndScreen_AddActionOnLeftClicked_00669280.cpp clean inline_asm +00/66/CInputTypeKeyboardKeyEvent_GetControlDirection_0066f610.cpp clean inline_asm +00/66/CInputTypeKeyboardKeyEvent_IsEventOfThisType_0066ee20.cpp clean inline_asm +00/66/CInputTypeKeyboardKeyEvent_IsEventOfThisTypeInQueue_0066f630.cpp clean inline_asm +00/66/CInputTypeKeyboardKeyEvent_operator_eq_0066ede0.cpp clean inline_asm +00/66/CInputTypeKeyboardKeyEvent_operator_eq_0066eea0.cpp clean inline_asm +00/66/CInputTypeKeyboardKeyEvent_operator_eq_0066f210.cpp clean inline_asm +00/66/CInputTypeMouseButtonEvent_GetControlDirection_0066f6b0.cpp clean inline_asm +00/66/CInputTypeMouseButtonEvent_GetEventTypeFromGeneralButtonType_0066f060.cpp clean inline_asm +00/66/CInputTypeMouseButtonEvent_IsEventOfThisType_0066eee0.cpp clean inline_asm +00/66/CInputTypeMouseButtonEvent_IsEventOfThisTypeInQueue_0066f6d0.cpp clean inline_asm +00/66/CInputTypeMouseMovementEvent_GetControlDirection_0066f8b0.cpp clean inline_asm +00/66/CInputTypeMouseMovementEvent_IsEventOfThisType_0066f420.cpp clean inline_asm +00/66/CInputTypeMouseMovementEvent_IsEventOfThisTypeInQueue_0066f440.cpp clean inline_asm +00/66/CInputTypeMouseMovementEvent_operator_eq_0066f2d0.cpp clean inline_asm +00/66/CInputTypeMouseMovementEvent_operator_eq_0066f480.cpp clean inline_asm +00/66/CInputTypeMouseWheelMovementDownEvent_GetControlDirection_0066f970.cpp clean inline_asm +00/66/CInputTypeMouseWheelMovementDownEvent_IsEventOfThisType_0066f5c0.cpp clean inline_asm +00/66/CInputTypeMouseWheelMovementEvent_GetControlDirection_0066f930.cpp clean inline_asm +00/66/CInputTypeMouseWheelMovementEvent_IsEventOfThisType_0066f4b0.cpp clean inline_asm +00/66/CInputTypeMouseWheelMovementEvent_IsEventOfThisTypeInQueue_0066f4d0.cpp clean inline_asm +00/66/CInputTypeMouseWheelMovementEvent_operator_eq_0066f510.cpp clean inline_asm +00/66/CInputTypeMouseWheelMovementUpEvent_GetControlDirection_0066f950.cpp clean inline_asm +00/66/CInputTypeMouseWheelMovementUpEvent_IsEventOfThisType_0066f540.cpp clean inline_asm +00/66/CInputTypeMouseWheelMovementUpEvent_operator_eq_0066f590.cpp clean inline_asm +00/66/CInputTypeXboxPadButtonEvent_GetControlDirection_0066f730.cpp clean inline_asm +00/66/CInputTypeXboxPadButtonEvent_IsEventOfThisType_0066f250.cpp clean inline_asm +00/66/CInputTypeXboxPadButtonEvent_IsEventOfThisTypeInQueue_0066f750.cpp clean inline_asm +00/66/CInputTypeXboxPadLeftStickEvent_GetControlDirection_0066f7d0.cpp clean inline_asm +00/66/CInputTypeXboxPadLeftStickEvent_IsEventOfThisType_0066f300.cpp clean inline_asm +00/66/CInputTypeXboxPadLeftStickEvent_IsEventOfThisTypeInQueue_0066f320.cpp clean inline_asm +00/66/CInputTypeXboxPadLeftStickEvent_operator_eq_0066f360.cpp clean inline_asm +00/66/CInputTypeXboxPadRightStickEvent_GetControlDirection_0066f840.cpp clean inline_asm +00/66/CInputTypeXboxPadRightStickEvent_IsEventOfThisType_0066f390.cpp clean inline_asm +00/66/CInputTypeXboxPadRightStickEvent_IsEventOfThisTypeInQueue_0066f3b0.cpp clean inline_asm +00/66/CInputTypeXboxPadRightStickEvent_operator_eq_0066f3f0.cpp clean inline_asm +00/66/CManager_CManager_0066eab7.cpp clean inline_asm +00/66/CManager_DeregisterAllScreens_0066ebe0.cpp clean inline_asm +00/66/CManager_DeregisterScreen_0066eb78.cpp clean inline_asm +00/66/CManager_GetAllRegisteredInventoryScreens_0066e9e9.cpp clean inline_asm +00/66/CManager_GetAllRegisteredScreens_0066e941.cpp clean inline_asm +00/66/CManager_GetScreenAtScrollIndex_0066e7e1.cpp clean inline_asm +00/66/CManager_InitialiseScrollBarIndices_0066ea85.cpp clean inline_asm +00/66/CManager_Open_0066ea46.cpp clean inline_asm +00/66/CManager_ProcessNextScreenAction_0066e845.cpp clean inline_asm +00/66/CManager_ProcessPreviousScreenAction_0066e8a1.cpp clean inline_asm +00/66/CManager_RegisterScreen_0066eb11.cpp clean inline_asm +00/66/CManager_RemoveAllPlayerModes_0066e985.cpp clean inline_asm +00/66/CManager_Update_0066e8ee.cpp clean inline_asm +00/66/CTCCoopSpirit_SwapToHero_0066ff20.cpp clean inline_asm +00/66/CTCPhysicsRigidBody_SetFacingAnglesFromRHSet_0066f9b0.cpp clean inline_asm +00/66/CThingCreatureBase_GetCombatTypeDef_00666d10.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/66/CThingCreatureBase_GetPCombatTypeDef_00664d40.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/66/std_Fill_n_ulong_0066ec31.cpp clean inline_asm +00/66/StoreFieldB4_0066f9e0.cpp clean inline_asm +00/66/Vector_AllocateUints_0066ec8a.cpp clean inline_asm +00/66/Vector_InsertValue_0066eda6.cpp clean inline_asm +00/66/x87_Exp_0066f9f0.cpp clean inline_asm +00/66/ZeroFields_0C_10_14_18_1C_20_24_0066fa30.cpp clean inline_asm +00/67/CTCCoopSpirit_OnCreate_006700f0.cpp clean inline_asm +00/68/CTCBase_GetDefinitionManager_006869d0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/68/CTCBase_GetPlayerManager_00686d40.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/68/CTCBase_GetPLocalSeed_00686db0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/68/CTCBase_GetWorld_00686a70.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/68/CTCBase_PeekComponent_00686d60.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/68/CTCBase_PeekPlayerManager_00686d50.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/68/CTCBase_PeekWorld_00686a80.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/68/CTCCreatureOpinionOfHero_IsWillingToStartFollowingHero_006818a0.cpp clean inline_asm +00/69/CCreatureActionBase_GetWorldMap_00692c10.cpp clean inline_asm +00/6b/CTCPhysicsBase_IsPhysicsMeshTypeCylinder_006b0170.cpp clean ptr_warning C4312 +00/6e/CGameScriptInterface_PostAddScriptedEntities_006e7460.cpp clean inline_asm +00/6f/ConsvalstdallocatorstdpairCWideStringconstfloatstdpairCWideStringconstfloatstd_pairCWideStringfloat_006f0560.cpp clean error C2821 ConsvalstdallocatorstdpairCWideStringconstfloatstdpairCWideStringconstfloatstd_pairCWideStringfloat_006f0560.cpp:4 C2821 first formal parameter to 'operator new' must be 'size_t' +00/6f/STL_DestroyAux_SymbolEntry_006fbcc0.cpp clean ptr_warning C4302,C4311,C4312 +00/70/CTCQuestCard_IsOptional_007022c0.cpp clean inline_asm +00/71/CCategory_GetHighlightedItemCountInCategory_00713290.cpp clean ptr_warning C4302,C4311 +00/72/CActionDoCreatureAction_GetActionName_007276b0.cpp clean ptr_warning C4312 +00/72/CTCPhysicsStandard_IsOnAnyStationarySurface_00723a60.cpp clean ptr_warning C4312 +00/73/CTCCombat_IsAbleToBeKnockedDown_007331a0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/73/CTCCombat_IsVulnerableToUnblockableAttack_007331c0.cpp clean ptr_warning C4302,C4311,C4312 +00/73/CTCCombat_UnsetBlocking_007338a0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/73/CTCCreatureModeManager_RemoveMode_0073aba0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/75/ConsvalstdallocatorstdpairCCharStringconststdpairlonglongstdpairCCharStringconst_pairlonglong_00755c60.cpp clean error C2821 ConsvalstdallocatorstdpairCCharStringconststdpairlonglongstdpairCCharStringconst_pairlonglong_00755c60.cpp:5 C2821 first formal parameter to 'operator new' must be 'size_t' +00/78/CTCCreatureGroupBoastingCrowd_OnHeroEnterBoastingArea_00784690.cpp clean inline_asm +00/78/CTCDivineWrath_GetDamageMultiplier_00789cc0.cpp clean inline_asm +00/78/global_Fillnunsignedlongunsignedintunsignedlong_00788c87.cpp clean inline_asm +00/79/CTCBulletTime_CancelForCutscene_0079c660.cpp clean inline_asm +00/7a/CWorldMap_PrepareForLoad_007a7d10.cpp clean inline_asm +00/82/CScriptedMapBrush_GetTheme_0082e330.cpp clean inline_asm +00/83/CFrontEndScreen_dtor_00835330.cpp clean inline_asm +00/84/CCreatureActionPerformExpression_PreventExpression_00842c00.cpp clean ptr_warning C4312 +00/84/CCreatureActionPerformExpressionExtended_PreventExpression_00842c50.cpp clean ptr_warning C4312 +00/85/CVirtualKeyboard_Cancel_00851860.cpp clean inline_asm +00/85/CVirtualKeyboard_Confirm_00851920.cpp clean inline_asm +00/85/CVirtualKeyboard_GetProfileName_00851890.cpp clean inline_asm +00/85/CWorld_LEASetDisplayMode_0085caf0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/85/CWorld_LEASetEditorMode_0085cb80.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/85/CWorld_LEASetExclusiveMode_0085ca80.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/86/ConsvalstdallocatorstdpairCWideStringconstfloatstdpairCWideStringconstfloatstd_pairCWideStringfloat_0086aaf0.cpp clean error C2821 ConsvalstdallocatorstdpairCWideStringconstfloatstdpairCWideStringconstfloatstd_pairCWideStringfloat_0086aaf0.cpp:4 C2821 first formal parameter to 'operator new' must be 'size_t' +00/88/CGameScriptInterface_EntityClearMaxNumberOfAttackers_0088ec70.cpp clean inline_asm +00/88/CGameScriptInterface_GiveHeroTitle_0088e110.cpp clean inline_asm +00/88/CGameScriptInterface_StartCountdownTimer_0088f8d0.cpp clean inline_asm +00/89/CGameScriptInterface_CameraCircleAroundPos_00892c20.cpp clean inline_asm +00/89/CGameScriptInterface_CameraCircleAroundThing_00892b50.cpp clean inline_asm +00/89/CGameScriptInterface_CameraMoveBetweenLookFromAndLookTo_00892610.cpp clean inline_asm +00/89/CGameScriptInterface_CameraMoveBetweenLookingAt_00892530.cpp clean inline_asm +00/89/CGameScriptInterface_CameraMoveBetweenLookingAt_008926d0.cpp clean inline_asm +00/89/CGameScriptInterface_CameraMoveToPosAndLookAtPos_00892410.cpp clean inline_asm +00/89/CGameScriptInterface_CameraMoveToPosAndLookAtThing_008924b0.cpp clean inline_asm +00/89/CGameScriptInterface_CameraUseCameraPoint_008922f0.cpp clean inline_asm +00/89/CGameScriptInterface_CameraUseCameraPoint_00892380.cpp clean inline_asm +00/89/CGameScriptInterface_DisplacementMonochromeEffectColourFadeTo_008901c0.cpp clean inline_asm +00/89/CGameScriptInterface_DisplayTutorial_0089e710.cpp clean inline_asm +00/89/CGameScriptInterface_EntityForceToLookAtCamera_0089d8b0.cpp clean inline_asm +00/89/CGameScriptInterface_EntityGetShotStrikePos_0089df70.cpp clean inline_asm +00/89/CGameScriptInterface_EntityResetAsPureAINarrator_0089b0b0.cpp clean inline_asm +00/89/CGameScriptInterface_EntitySetAsPickLocked_0089a390.cpp clean inline_asm +00/89/CGameScriptInterface_EntitySetCombatAbility_0089aff0.cpp clean inline_asm +00/89/CGameScriptInterface_GetDeathRecoveryMarkerName_00896ee0.cpp clean inline_asm +00/89/CGameScriptInterface_GetDistanceHeroCanBeHeardFrom_00899700.cpp clean inline_asm +00/89/CGameScriptInterface_GetFurthestWithScriptName_0089a6e0.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroAge_00899560.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroAttractiveness_00899930.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroExperienceAvailableToSpend_008997e0.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroFatness_00899850.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroHasChildren_00897bd0.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroHasCurrentMarriage_00897b10.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroHasDivorcedMarriage_00897b70.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroHasMarried_00897aa0.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroHasMurderedWife_00897c30.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroMorality_00899f90.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroMoralityCategory_0089a000.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroRenownLevel_00899140.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroRoughExperienceLevel_00899770.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroScariness_008998c0.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroSkillLevel_00899400.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroStatLevel_00899290.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroStatMax_00899310.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroStrengthLevel_00899390.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroTargetedThing_0089a490.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroWillEnergy_0089a150.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroWillEnergyLevel_008999a0.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroWillEnergyMax_0089a1b0.cpp clean inline_asm +00/89/CGameScriptInterface_GetHeroWillLevel_00899470.cpp clean inline_asm +00/89/CGameScriptInterface_GetNearestWithScriptName_0089a510.cpp clean inline_asm +00/89/CGameScriptInterface_GiveHeroMorality_00899ef0.cpp clean inline_asm +00/89/CGameScriptInterface_GiveHeroRenownPoints_008990c0.cpp clean inline_asm +00/89/CGameScriptInterface_GiveHeroTutorial_00898a50.cpp clean inline_asm +00/89/CGameScriptInterface_IncreaseHeroRenownLevel_00899220.cpp clean inline_asm +00/89/CGameScriptInterface_IsEntityMarriable_00897a30.cpp clean inline_asm +00/89/CGameScriptInterface_IsEntityMarriedToHero_008979c0.cpp clean inline_asm +00/89/CGameScriptInterface_IsEntityStealable_0089a330.cpp clean inline_asm +00/89/CGameScriptInterface_IsEntityWieldingWeapon_008974f0.cpp clean inline_asm +00/89/CGameScriptInterface_IsHeroAllowedHenchmenInCurrentRegion_00891ea0.cpp clean inline_asm +00/89/CGameScriptInterface_IsHeroAllowedHenchmenInRegion_00891ef0.cpp clean inline_asm +00/89/CGameScriptInterface_IsHeroRenownLevelFull_008991a0.cpp clean inline_asm +00/89/CGameScriptInterface_IsPlayerZTargetingThing_00899b10.cpp clean inline_asm +00/89/CGameScriptInterface_IsQuestStartScreenActive_0089ad90.cpp clean inline_asm +00/89/CGameScriptInterface_MsgIsGameInfoClickedPast_00894370.cpp clean ptr_warning C4312 +00/89/CGameScriptInterface_MsgOnLevelLoaded_0089ac10.cpp clean inline_asm +00/89/CGameScriptInterface_RemoveDeadCreature_00894490.cpp clean inline_asm +00/89/CGameScriptInterface_RemoveHeroHairstyle_0089b4d0.cpp clean inline_asm +00/89/CGameScriptInterface_ResetPlayerCreatureOnlyTarget_00899e90.cpp clean inline_asm +00/89/CGameScriptInterface_SetAbilityAvailability_0089d7c0.cpp clean inline_asm +00/89/CGameScriptInterface_SetDeathRecoveryMarkerName_00896f60.cpp clean ptr_warning C4312 +00/89/CGameScriptInterface_SetHeroAbleToGainExperience_00899060.cpp clean inline_asm +00/89/CGameScriptInterface_SetHeroAge_008994e0.cpp clean inline_asm +00/89/CGameScriptInterface_SetHeroAsApprentice_00899680.cpp clean inline_asm +00/89/CGameScriptInterface_SetHeroAsTeenager_008995d0.cpp clean inline_asm +00/89/CGameScriptInterface_SetHeroWillEnergyAsAbleToRefill_00899a90.cpp clean inline_asm +00/89/CGameScriptInterface_SetHeroWillEnergyLevel_00899a10.cpp clean inline_asm +00/89/CGameScriptInterface_SetPlayerCreatureOnlyTarget_00899e10.cpp clean inline_asm +00/8a/CGameScriptInterface_ClearIsGossipForPlayer_008a7930.cpp clean ptr_warning C4302,C4311 +00/8a/CGameScriptInterface_EntityDecapitate_008a8e40.cpp clean inline_asm +00/8a/CGameScriptInterface_EntitySetAttackThingImmediately_008a8eb0.cpp clean inline_asm +00/8a/CGameScriptInterface_GetAllCreaturesExcludingHero_008a89d0.cpp clean inline_asm +00/8a/CGameScriptInterface_GetAllCreaturesInAreaWithScriptName_008a86c0.cpp clean inline_asm +00/8a/CGameScriptInterface_GetAllThingsWithDefNameByDistanceFrom_008a9c40.cpp clean inline_asm +00/8a/CGameScriptInterface_GetBestTimeGuessTheAddition_008a17d0.cpp clean inline_asm +00/8a/CGameScriptInterface_GetBestTimePairs_008a1590.cpp clean inline_asm +00/8a/CGameScriptInterface_GetBestTimeSorting_008a15f0.cpp clean inline_asm +00/8a/CGameScriptInterface_GetGossipSize_008a9fc0.cpp clean ptr_warning C4302,C4311 +00/8a/CGameScriptInterface_GetGossipVillagesSize_008aa220.cpp clean ptr_warning C4302,C4311 +00/8a/CGameScriptInterface_RepopulateVillage_008a1c50.cpp clean inline_asm +00/8a/CGameScriptInterface_SetReadableObjectTextTag_008a0fd0.cpp clean inline_asm +00/8a/CGameScriptInterface_SetThingAsConscious_008a9610.cpp clean inline_asm +00/8f/CAIStateGroupBase_GetPWorldSeed_008fcfd0.cpp clean ptr_warning C4312 +00/98/CPixelShader_CountReferences_00987ee0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderBase_GetDebugName_00987cf0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_AllocateLights_0098b420.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_ApplyVertexShader_00988020.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_Constructor_0098ab00.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_CreateResource_0098b860.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_Destructor_0098abf0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_DisablePixelShaders_00988190.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_DisableVertexShaders_009880e0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_EnablePixelShaders_00988110.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_EnableVertexShaders_00987fe0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_Initialise_0098acf0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_MatrixSetters_009881f0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_OnPostViewportChanged_009880d0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_RegisterVertexShaderConstantLayout_0098b230.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_ResetLight_00988640.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_ResetWorldTransform_00988290.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_SetCameraPos_00988320.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_SetVertexShaderConstantLayout_0098b5e0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/98/CShaderRenderManager_SetVSConstants_0098b930.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_Uninitialise_0098a7a0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_UnregisterVertexShaderConstantLayouts_0098b280.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_UpdateAmbient_00989760.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_UpdateFogColour_009897c0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_UpdateLightGlobals_0098a760.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_UpdatePixelShader_00988a20.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CShaderRenderManager_UpdatePixelShaderConstants_00989bf0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CThreadedFile_AddObject_0098f350.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CThreadedFile_Close_0098ec40.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CThreadedFile_Constructor_0098dfd0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CThreadedFile_FinishAndGetFileHandle_0098ec70.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CThreadedFile_Open_0098e1e0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CThreadedFile_RemoveObject_0098ec90.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CThreadedFile_SetObjectPriority_0098f0e0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CThreadedFile_WaitForObjectToFinish_0098f370.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CThreadedFileManager_OnFinishObject_0098ebc0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CThreadedFileManager_ProcessDirtyDiscError_0098e1b0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CThreadedFileManager_RemoveObject_0098eb20.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CThreadedFileManager_SetObjectPriority_0098eff0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/CVertexShaderConstantLayout_PeekName_0098d6f0.cpp clean ptr_warning C4302,C4311,C4312 +00/98/IndexedCharArray_PushBack_0098db20.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/98/STLPendingObjectTree_Find_0098fe70.cpp clean ptr_warning C4302,C4311,C4312 +00/98/STLPendingObjectTree_IteratorIncrement_0098f9d0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_CompareFiles_00998f60.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_ConvertDirectoryAndFilenameToPath_009979e0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_ConvertPathToFullPath_00999140.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_CopyFileW_00998e70.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_ExpandPath_00998c90.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_FileQueries_009990b0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_FileScan_00999760.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_FileScanStrings_00999c50.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_GetAbsoluteFilePath_00998250.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_GetAbsoluteFilePath_00998c30.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_GetCopyFilename_00997bb0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_GetCurrentPath_00997f80.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_GetFileModifiedTime_009992a0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_GetProjectPath_00997510.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_IsFilePathSyntaxValid_00998a30.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_ParserReadPastComponent_00998010.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_PathExists_00999230.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_PathHelpers_009975b0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_PathParser_00998170.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_SetCurrentPath_009974f0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_SplitPath_00997620.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CAFile_ValidatePathnameIsOnWriteableDrive_00999970.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CASoundManager_UnknownSetEnabled_009915e0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CBufferedFileDataOutputStream_00994520.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_AddCharStringAndCharString_0099f570.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_AddCharStringAndLiteral_0099f600.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_AddLiteralAndCharString_0099f690.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_AllocStringData_0099ea60.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_AppendChar_0099f180.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_AppendCharString_0099f0a0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_AppendLiteral_0099f100.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_AssignChar_0099f040.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_CCharString_0099ec30.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_Constructor_0099ebf0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_ConversionOperator_0099e4c0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_CopyAssignment_0099efb0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_DefaultConstructor_0099e4b0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_Destructor_0099eae0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_Edit_0099f2a0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/99/CCharString_FindCharPos_0099e5a0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_FromFile_0099fa50.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_GetAsLowerCase_0099ed10.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_GetAsUpperCase_0099ee20.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_GetSubString_0099ec70.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_MakeOwnCopyOfData_0099eaf0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_MakeOwnCopyOfDataOrEmptyString_0099eb40.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_NotEqual_0099e900.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_NotEqualChar_0099e520.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_operator_0099e960.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_operator_0099efe0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_operatorcharconstptr2_0099af10.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_operatorcharconstptr_0099af00.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_RemoveWhiteSpaceBegin_0099f450.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_RemoveWhiteSpaceBeginEnd_0099f560.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_RemoveWhiteSpaceEnd_0099f4d0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_SetAsSubString_0099ef40.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_SetCharTyped_0099f390.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_Sprintf_0099f1f0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/99/CCharString_ToWideString_0099e480.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharString_UnassignString_0099e9b0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CCharStringData_ReadFromFile_0099e4d0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataInputStream_CDataInputStream_00993b20.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/99/CDataInputStream_CopyTo_009964e0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/99/CDataInputStream_ReadCompressed_00995fb0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataInputStream_ReadLine_00994b50.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataInputStream_ReadNullTerminatedString_00994c80.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataInputStream_ReadNullTerminatedWString_00994d20.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataInputStream_ReadPresizedString_00996390.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataInputStream_ReadUntilCharacter_00994dc0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataInputStream_ReadUntilWhiteSpace_00994e90.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataInputStream_ReadWhiteSpace_00994f60.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataInputStream_ReadWithSrcChunkOverflow_00993ca0.cpp clean inline_asm +00/99/CDataInputStream_ReadZlibCompressed_00996230.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataInputStream_VectorReaders_00995030.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataOutputStream_CopyFrom_00996970.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataOutputStream_StringWriters_009959f0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataOutputStream_WriteCBYTE_00993ea0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataOutputStream_WriteCBYTE_00993eb0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataOutputStream_WriteDateAndTime_00994180.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataOutputStream_WriteEBOOL_00993e30.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataOutputStream_WriteNullTerminatedString_00995ae0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataOutputStream_WriteRepeatedByte_00994230.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDataOutputStream_Writers_00993e50.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDiskFileWin32_Clone_0099ae00.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDiskFileWin32_Close_0099A920.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDiskFileWin32_Constructor_0099AD80.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDiskFileWin32_GetFileInformation_0099a5a0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDiskFileWin32_GetPosition_0099abd0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDiskFileWin32_MovePosition_0099ac90.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDiskFileWin32_Open_0099a6a0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDiskFileWin32_Read_0099a960.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDiskFileWin32_SetLength_0099a560.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDiskFileWin32_SetPosition_0099abf0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CDiskFileWin32_Write_0099aa90.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CFileDataInputStream_CFileDataInputStream_00994700.cpp clean inline_asm +00/99/CFileDataInputStream_Close_00994300.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CFileDataInputStream_dtor_00994780.cpp clean inline_asm +00/99/CFileDataInputStream_InternalReadNextSourceChunk_00994360.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CFileDataInputStream_InternalReadUnbuffered_009943d0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CFileDataOutputStream_Close_00994470.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CFileDataOutputStream_Constructor_00994400.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CFileDataOutputStream_Destructor_00994430.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CFileDataOutputStream_GetPosition_00994460.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CFileDataOutputStream_SetPosition_00994450.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CFileDataOutputStream_Write_00994480.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CFrontEndScreen_AddActionOnLeftClicked_00992470.cpp clean inline_asm +00/99/CMemoryDataInputStream_ReadCompressed_00995b10.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CMemoryDataInputStream_ReadNullTerminatedString_00995d30.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CMemoryDataInputStream_ReadNullTerminatedWString_00994960.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CMemoryDataOutputStream_Clear_00996b60.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CMemoryDataOutputStream_Close_009945c0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CMemoryDataOutputStream_CMemoryDataOutputStream_00996af0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CMemoryDataOutputStream_CopyDataToString_00995e80.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/99/CMemoryDataOutputStream_Destructor_00996b30.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CMemoryDataOutputStream_GetPosition_009945b0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CMemoryDataOutputStream_SetPosition_00994b10.cpp clean ptr_warning C4302,C4311,C4312 +00/99/ConsvalstdallocatorstdpairEHeroMorphTypeCParticleMorphsCEntrystdpairEHeroMorphTy_CEntryconst_00996bc0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CSystemRegistry_Constructor_0099cf10.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CSystemRegistry_Destructor_0099cf70.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CSystemRegistry_Values_0099cf80.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CSystemRegistry_WriteBOOL_0099d100.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_AddLiteralAndWideString_0099bff0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_AddWideStringAndLiteral_0099bf30.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_AddWideStrings_0099be70.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_AllocStringData_0099b3c0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_AppendWideString_0099b8d0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_AssignLiteral_0099b800.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_CharOperators_0099b0f0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_Compare_0099af20.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/99/CWideString_Constructor_0099aed0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_ConversionOperator_0099af10.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_CopyAssignment_0099b7d0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_CopyConstructor_0099b720.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_CreateFromCharString_0099b6a0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_CWideStringfromLiteral_0099b6b0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_Destructor_0099b510.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_FindCharPos_0099b070.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_FromCharConstructor_0099b440.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_GetByteLength_0099b240.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_GetLength_0099b220.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_Less_0099af70.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_LiteralConstructor_0099b6b0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_MakeOwnCopyOfData_0099b560.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_NotEqualWide_0099b150.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_operatorplusequalswchar_0099b940.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_operatorwchartconst_0099b800.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_SetLength_0099bc50.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_Sprintf_0099ba70.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/99/CWideString_ToCharString_0099b2c0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/CWideString_UnassignString_0099b4d0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/DataInputStream_ReadWithSrcChunkOverflow_00993ca0.cpp error C2664 error C2664 DataInputStream_ReadWithSrcChunkOverflow_00993ca0.cpp:26 C2664 'void CDataInputStream::InternalReadNextSourceChunk(const fable_u8 *&,long &)': cannot convert argument 1 from 'fable_u8 +00/99/ErrorDialog_Hyperlink_0099d5b0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/ErrorDialog_InitExternalStrings_0099e260.cpp clean ptr_warning C4312 +00/99/ErrorDialog_Lookups_0099d2c0.cpp clean ptr_warning C4302,C4311 +00/99/GFCharStringNumbers_0099e620.cpp clean ptr_warning C4302,C4311,C4312 +00/99/Global_ClearDirtyExitFlag_0099dc50.cpp clean ptr_warning C4312 +00/99/global_ConvertPathToFullPath_00999110.cpp clean ptr_warning C4302,C4311,C4312 +00/99/Global_GFBoolToCharString_0099f9b0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/Global_GFHexCharStringToInt_0099e750.cpp clean ptr_warning C4302,C4311,C4312 +00/99/Global_GFIntToCharString_0099f830.cpp clean ptr_warning C4302,C4311,C4312 +00/99/Global_GFUIntToCharString_0099f8b0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/NFile_CalcCurrentPath_009974a0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/NFile_GetItemFromPath_00997b40.cpp clean ptr_warning C4302,C4311,C4312 +00/99/STLPendingObjectTree_KeyErase_00990cd0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/STLThreadedUIDTree_find_009902d0.cpp clean ptr_warning C4302,C4311,C4312 +00/99/STLThreadedUIDTree_IteratorErase_00990430.cpp clean ptr_warning C4302,C4311,C4312 +00/99/STLThreadedUIDTree_KeyErase_00990d60.cpp clean ptr_warning C4302,C4311,C4312 +00/99/String_Sizeof_0099e500.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9a/CBankFileManager_BankExists_009a80b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankFileManager_Close_009a7990.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankFileManager_CloseRetailBank_009a78d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankFileManager_Constructor_009a7ac0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankFileManager_Destructor_009a7b70.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankFileManager_HeaderPaths_009a7e20.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankFileManager_OpenIniFile_009a8170.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankFileManager_OpenRetailBank_009a8840.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankFileManager_ReadQuotedString_009a77d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankInfoMap_Erase_009aa7c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankInfoMap_Index_009ac5d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankInfoMap_InsertNode_009ab120.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankInfoMap_InsertUnique_009ab1f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankInfoMap_InsertUniqueHint_009abb10.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankInfoMap_IteratorAdvance_009a9e20.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankInfoMap_IteratorRetreat_009aa320.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankInfoMap_LowerBound_009aadb0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBankManagerWidePathSet_Erase_009aa880.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBasicRetailBankAliasMap_Erase_009aa830.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBasicRetailBankAliasMap_Index_009ac700.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBasicRetailBankAliasMap_InsertNode_009ab2f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBasicRetailBankAliasMap_InsertUnique_009ab3f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBasicRetailBankAliasMap_InsertUniqueHint_009abdb0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBasicRetailBankAliasMap_IteratorAdvance_009a9860.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBasicRetailBankAliasMap_IteratorRetreat_009aa370.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBasicRetailBankAliasMap_LowerBound_009aae00.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBasicRetailBankManager_AddBankAlias_009a8150.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBasicRetailBankManager_SetNavigatorEnabled_009a76d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CBasicStringchar_SetString_009a0390.cpp clean error C2821 CBasicStringchar_SetString_009a0390.cpp:5 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9a/CContainedBankMap_CreateNode_009aa9e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CContainedBankMap_Erase_009aa780.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CContainedBankMap_InsertNode_009aaf70.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CContainedBankMap_InsertUnique_009ab020.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CContainedBankMap_InsertUniqueHint_009ab870.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CContainedBankMap_IteratorAdvance_009a9820.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CContainedBankMap_IteratorRetreat_009aa2d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CContainedBankMap_LowerBound_009aad60.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CCountedPointer_CThreadedFile_Reset_009a9c80.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CCountedPointer_RetailBank_DeleteData_009a90b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CCountedPointerDriveManager_Reset_009a73d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CCountedPointerFontManager_Reset_009a7340.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CCountedPointerGraphicBankManager_Reset_009a71f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CCountedPointerMeshBankManager_Reset_009a7260.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CCountedPointerProfileManager_Reset_009a7510.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefClassBase_InternalLoadBinary_009acb20.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefClassBase_InternalSaveBinary_009acab0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefClassBase_UnloadResource_009ace90.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_ClearInstantiatedDefs_009ad950.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_ClearSymbols_009acb10.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_CompileDefinition_009ae7c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_CompileString_009af1e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_CompileSubDefinition_009ae150.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_CompressDefinitionBlock_009ae390.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9a/CDefinitionManager_CreateSymbolsFromPathList_009aef60.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_DecompressDefinitionData_009ad5f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_DoCompilePathList_009af480.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_Error_009ac9f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_GetDefGlobalIndexFromName_009ad410.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_GetPDefClassInfo_009ad580.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_GetPDefClassInfoConst_009ad2e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_GetPDefFromClassIndex_009ade10.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_GetPDefFromGlobalIndex_009ada10.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_GetPDefFromGlobalIndexConst_009ad9e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_GetPDefFromInstantiationName_009ada90.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_GetPDefFromInstantiationNameConst_009ada40.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_InstantiateDef_009adc90.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9a/CDefinitionManager_InstantiateSubDef_009adae0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9a/CDefinitionManager_LoadBinaryDefinitions_009afb90.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9a/CDefinitionManager_LoadDef_009ad6e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_ParseEnumForSymbols_009acee0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_ParseStringForSymbols_009ade90.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_RemoveUnusedMemory_009ae5f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CDefinitionManager_SaveBinaryDefinitions_009af7a0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9a/CDefinitionManager_ScrambleDefs_009ac9e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CFontBank_Preload_009a0b20.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CParentDefClassBase_GetPSubDefInfo_009ad510.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CParentDefClassBase_GetPSubDefInfoConst_009ad4a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CParentDefClassBase_InternalLoadBinary_009aed70.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CRetailBank_Destructor_009ac7c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CRetailBankList_Clear_009aae50.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_Accessors_009a5010.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_ActivateWindow_009a5a50.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_DefaultErrorFunc_009a5260.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_DeviceResetCallbacks_009a52b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_DisplayCriticalMessage_009a4ed0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_ExceptionHandler_009a5020.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_InitApplicationName_009a5340.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_InitGUIDFromName_009a52f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_Initialise_009a6610.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_InitSystemColours_009a5490.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_InitWindow_009a64b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_IsApplicationActive_009a57b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_Lifetime_009a7440.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_ProcessWindowMessages_009a4f20.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_SetAsActive_009a5270.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_SetExclusiveMode_009a59a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_SetHasInputFocus_009a5a30.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_Uninitialise_009a60d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_Update_009a6460.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_UpdateActive_009a6370.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CSystemManager_WinProcedure_009a5b60.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/CUserProfileManager_GetAutoSaveFileName_009a76a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/Global_GFGetSystemManager_009a4ec0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/NBankFileManager_CContainedBankMap_Index_009ac530.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/NBankFileManager_CRetailBank_Constructor_009ac4d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/NBankFileManager_SetHeaderDir_009a76c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9a/STLBankMaps_Find_009ab4f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CArrayVector_DeallocateUnusedMemory_009b6e00.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CArrayVector_Definition_DeallocateMemory_009b4a90.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CArrayVector_SaveData_DeallocateMemory_009b4ae0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CCountedPointer_CSymbolMap_Reset_009b1f50.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDefinitionManager_AddDefClass_009b0ac0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDefinitionManager_CDefinitionManager_009b0470.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDefinitionManager_CheckDependencies_009b05f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDefinitionManager_Compile_009b08c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDefinitionManager_Destructor_009b02e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_ChangePresentationInterval_009bfe70.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_ClearRenderTarget_009be420.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_CreateDevice_009bf7e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_DestroyDevice_009be390.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_DeviceResetCallbacks_009bf5a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_DeviceResetPhases_009bf500.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_FormatChecks_009be8f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_GetBestAlphaTextureFormat_009be6c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_GetBestBooleanAlphaTextureFormat_009be750.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_GetBestCompressedAlphaTextureFormat_009be870.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_GetBestCompressedBooleanAlphaTextureFormat_009be8b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_GetBestCompressedOpaqueTextureFormat_009be830.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_GetBestOpaqueTextureFormat_009be610.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_GetBestSignedTextureFormat_009be590.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_GetClosestDepthBufferFormat_009bebb0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_GetClosestMode_009bf600.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_GetCurrentWindowedSettings_009bed10.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_GetPixelShaderVersion_009bee00.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_GetRenderTargetDimensions_009bedc0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_GetVVisibleTelevisionScreenArea_009bece0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_InitVirtualPixels_009be980.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_IsSupportingCompressedTextures_009be800.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_IsTextureBitDepthSupported_009be530.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_ModeChanges_009bff40.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_NativeRender_009bef20.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_RenderTargets_009bf160.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_ResetDevice_009bfb30.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_ResetHelpers_009be2d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_ResizeWindowToBackBufferSize_009be490.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_SetGamma_009beaa0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_SetViewport_009bf490.cpp clean inline_asm +00/9b/CDisplayManager_SetViewportInteger_009bef80.cpp clean inline_asm +00/9b/CDisplayManager_SetVirtualCoordsAsResolutionIndependant_009bea10.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_SmallQueries_009be380.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CDisplayManager_SwapScreens_009beeb0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CParentDefClassBase_AddSubDef_009b00d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CParentDefClassBase_InternalSaveBinary_009b0200.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9b/CParentDefClassBase_RemoveSubDef_009b0170.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CParsedItem_Accessors_009b9870.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CParsedItem_ConvertToString_009b98b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CParsedItem_InitAsFloat_009b9770.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CParsedItem_InitAsInteger_009b9700.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CPersistContext_CheckUnknownSymbols_009bbbf0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CPersistContext_CPersistContext_009baa30.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CPersistContext_Init_Save_009baa50.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CPersistContext_PersistError_009baa90.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CPersistContext_Sections_009bab60.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CPersistContext_TextConstructor_009badd0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CPersistContext_TransferObjectLoadText_009bba40.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9b/CPersistContext_TransferObjectSaveText_009bad60.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_CalcInQuotesTyped_009b9570.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_Comments_009ba830.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_Error_009b95f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_Helpers_009b95a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_InitTyped_009b9530.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_NextItemExists_009ba4f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_PeekNextItem_009ba8c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_PeekNextItemType_009ba900.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_ReadAsInteger_009ba540.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_ReadAsString_009ba6f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_ReadAsStringUntilPastString_009ba3e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_ReadAsStringUntilString_009ba110.cpp clean ptr_warning C4302,C4311 +00/9b/CStringParser_ReadAsStringUntilString_009ba170.cpp clean ptr_warning C4302,C4311 +00/9b/CStringParser_ReadAsStringUntilString_009ba330.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_ReadAsStringUntilWhitespace_009ba4a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_ReadAsSymbol_009ba790.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_ReadAsUHUGE_009ba5b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_Readers_009ba2d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_ReadNextItem_009ba080.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_ReadNextItemAsQuotedString_009ba1d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_ReadNextNumber_009b9ee0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_ReadNextString_009b9fc0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_ReadNextSymbol_009b99c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_SetStringPos_009b9940.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_SkipPastNextItemIfIs_009b9e00.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_SkipPastString_009b9ba0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_SkipPastWholeString_009b9c00.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_SkipUntilString_009b9a20.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_SkipUntilWhitespace_009b9ce0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_SkipUntilWholeString_009b9aa0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CStringParser_SkipWhitespace_009b9c60.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CThreadedFileCallback_OnFileReadBegin_009bdc00.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CThreadedFileCallback_OnFileReadCompletion_009bdc10.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CVectorMap_ChildDef_DeallocateUnusedMemory_009b79b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CVectorMap_CompressedOffset_DeallocateUnusedMemory_009b7b10.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CVectorMapCompressedOffsets_UpperBound_009b1ff0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CVectorMapDefClassInfo_LowerBound_009b2230.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CVectorMapDefClassInfo_LowerBoundConst_009b2290.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CVectorMapNameIndex_LowerBoundConst_009b21f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CVectorMapSubDefInfo_LowerBound_009b20d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CVectorMapSubDefInfo_LowerBoundConst_009b2130.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/CVectorMapSubDefInfo_UpperBound_009b2190.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/global_InitClearMode_009baa70.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/global_StaticLoadXMVCode_009bcf70.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_AdjustHeap_CompressedOffset_009b3060.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_AdjustHeap_IndexEntry_009b2ff0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_AdjustHeap_SubDefEntry_009b2f30.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_AdjustHeap_WideString_009b2ea0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_InsertionSort_CompressedOffset_009b56f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_InsertionSort_DefClassEntry_009b8f70.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_InsertionSort_SubDefEntry_009b5670.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_InsertionSort_WideString_009b68d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_Introsort_CompressedOffset_009b7fa0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_Introsort_WideString_009b7cf0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_IntrosortLoop_DefClassEntry_009b9320.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_IntrosortLoop_SubDefEntry_009b7dd0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_LinearInsert_CompressedOffset_009b46b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_MakeHeap_CompressedOffset_009b3cf0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_MakeHeap_IndexEntry_009b3c70.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_MakeHeap_SubDefEntry_009b3c00.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_MakeHeap_WideString_009b3bb0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_PartialSort_CompressedOffset_009b6be0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_PartialSort_DefClassEntry_009b9030.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_PartialSort_IndexEntry_009b6b70.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_PartialSort_SubDefEntry_009b6ad0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_PartialSort_WideString_009b6a40.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_Partition_CompressedOffset_009b3900.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_PushHeap_CompressedOffset_009b1d80.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_PushHeap_IndexEntry_009b1d10.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_PushHeap_SubDefEntry_009b1ca0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_PushHeap_WideString_009b1c30.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_RecompiledDefTreeErase_009b40f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_Sort_CompressedOffset_009b85a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_Sort_DefClassEntry_009b9490.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_Sort_SubDefEntry_009b84c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_Sort_WideString_009b8440.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_SortHeap_CompressedOffset_009b5c10.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_SortHeap_IndexEntry_009b5bb0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_SortHeap_SubDefEntry_009b5af0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_SortHeap_WideString_009b5a80.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_UnguardedInsertionSort_CompressedOffset_009b2db0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_UnguardedInsertionSort_DefClassEntry_009b80c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_UnguardedInsertionSort_WideString_009b2ac0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_UnguardedInsertionSortAux_SubDefEntry_009b2cb0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_UnguardedLinearInsert_CompressedOffset_009b1b00.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_UnguardedPartition_DefClassEntry_009b8630.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_UnguardedPartition_SubDefEntry_009b3800.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STL_UnguardedPartition_WideString_009b3580.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STLIndexSort_insertionsort_009b69a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STLIndexSort_introsortloop_009b7ed0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STLIndexSort_linearinsert_009b5920.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STLIndexSort_sort_009b8520.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STLIndexSort_unguardedinsertionsortaux_009b2d40.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STLIndexSort_unguardedlinearinsert_009b1aa0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STLIndexSort_unguardedpartition_009b3890.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STLSort_DefClassInfo_linear_insert_009b8c00.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STLSort_DefClassInfo_unguarded_linear_insert_009b76f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STLVector_DefClassEntry_M_insert_overflow_009b8dd0.cpp clean ptr_warning C4302,C4311,C4312 +00/9b/STLVector_DefClassEntry_push_back_009b9170.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_AdjustUpdateEntries_009cba70.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_CBankFile_009cd480.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_Close_009ce360.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_CloseBankFileImpl_009cd850.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_CreateSymbolMap_009cc530.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_Destructor_009ce180.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_EntryAccessors_009cc210.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_EntryExists_009cc190.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_EntrySetUpdate_009cc300.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_FileAccessors_009cba40.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_FindIndexBySymbol_009ccdc0.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_FindIndexBySymbolCRC_009cc410.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_GetAlignment_009cbb80.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_GetEntryDataOffset_009cc270.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_GetEntrySymbol_009cc1d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_GetEnumerationType_009cbd10.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_GetMaxEntryIndex_009cc350.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_GetUnusedSpace_009cc4c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_IsDiskFile_009cbc40.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_NotifyBankOpened_009cbc00.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_NotifyBankSize_009cbbf0.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_NotifyEntryAdded_009cbc20.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_NotifyEntryRemoved_009cbc30.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_NotifyEntryUpdated_009cbc10.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_ReadData_009cbf60.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_ReadEntryData_009cc2a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_RecoveryFile_009cbfe0.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFile_SymbolHelpers_009cbaa0.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CBankFileEntryUpdateData_ReadInfo_009cbe90.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CDisplayManager_Constructor_009c0880.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CDisplayManager_Destructor_009c01c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CDisplayManager_Init_009c0e50.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CDisplayManager_ProcessLostDevice_009c00c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/CFileInstaller_Constructor_009c5890.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9c/CFileInstaller_DecompressionThreadMain_009c4230.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9c/CFileInstaller_Destructor_009c5170.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9c/CFileInstaller_ThreadMain_009c5690.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9c/CFileInstaller_Workers_009c5870.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9c/CQuickDrawTriInfo_VectorDestructor_009c2f50.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/DisplayModeVectors_InsertOverflow_009c2ca0.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/FileInstallerBufferArray_009c7150.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9c/FileInstallerClearBuffers_009c5110.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9c/FileInstallerDestroyBufferRange_009c6d60.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9c/InstallerActiveFileList_Clear_009c71a0.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9c/InstallerCountedFileList_Clear_009c6cc0.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9c/NGameText_CDataBank_BeginLoading_009c8630.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/NGameText_CDataBank_CloseTextAndGroups_009c8660.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/NGameText_CDataBank_Constructor_009c85a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/NGameText_CDataBank_Destructor_009c87c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/NGameText_CDataBank_GetEnumerationType_009c79e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/NGameText_CDataBank_GetLastModifiedTime_009cb710.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/NGameText_CDataBank_GetPGroupEntry_009c8890.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/NGameText_CDataBank_GetPTextEntry_009c9070.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/NGameText_CTextEntry_GetText_009c7a40.cpp clean ptr_warning C4302,C4311,C4312 +00/9c/NGameText_ResourceEntries_Sizeof_009c7d90.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9c/NGameText_ResourceEntries_UnloadResource_009c7e00.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/BankSymbolMap_LowerBound_009d20d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFile_ReleaseBankSymbols_009d3fb0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsync_AsyncUpdatePending_009d68b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsync_CBankFileAsync_009d5f80.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsync_Close_009d58a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsync_Destructor_009d6490.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsync_DiscardAsyncData_009d67e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsync_GetAsyncEntry_009d58d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsync_OpenFileReadOnly_009d5820.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsync_OpenFileReadWrite_009d5200.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsync_OpenReadWrite_009d51d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsync_ProcessDisposalList_009d6730.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsync_RestartAbortedReads_009d5a90.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsync_SetLoadingMemoryFailureHandler_009d66a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsyncData_Accessors_009d54e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsyncData_CanRelocateData_009d55d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsyncData_CBankFileAsyncData_009d5df0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsyncData_DiscardAsyncData_009d68a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsyncData_GetData_009d5530.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsyncData_Lifetime_009d5410.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9d/CBankFileAsyncData_OnFileReadBegin_009d5440.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsyncData_OnFileReadCompletion_009d54a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsyncData_RelocateData_009d5670.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsyncData_SetupBuffer_009d52d0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9d/CBankFileAsyncData_WaitUntilFinished_009d5b60.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsyncEntry_BeginReadData_009d6060.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsyncEntry_BeginReadDataAutoAlloc_009d6100.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CBankFileAsyncEntry_ReadDataNonAsync_009d52b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CDefStringTable_AddString_009d4f90.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CDefStringTable_CDefStringTable_009d4db0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CDefStringTable_Destructor_009d4e90.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CDefStringTable_GetString_009d49b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CDefStringTable_GetStringCRC_009d4a20.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CDefStringTable_LoadTable_009d4c30.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CDefStringTable_ReadTable_009d4ab0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CDefStringTable_SaveTable_009d4ce0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CDefStringTable_SetTableFilename_009d4a40.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CDefStringTable_Unlock_009d4f10.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CDefStringTable_WriteTable_009d4a50.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CLoadingMemoryFailureHandler_Handler_009d5eb0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CMovableResource_Verify_009d5100.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CQuickDrawTriInfo_CopyConstructor_009de890.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRegion_SetMiniMapRegionExitTextOffsetX_009d5060.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRenderManager2D_Add2DLine_009dc2c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRenderManager2D_Add2DTri_009dc030.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRenderManager2D_AddText_009db700.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRenderManager2D_ClearRenderTarget_009d8cf0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9d/CRenderManager2D_Constructor_009d9a30.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRenderManager2D_Draw2DBox_009de3f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRenderManager2D_Draw2DBoxColours_009dda50.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRenderManager2D_Draw2DBoxOutline_009de140.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRenderManager2D_Draw2DBoxWithZ_009ddc60.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRenderManager2D_Draw2DCircle_009de2a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRenderManager2D_Draw2DLine_009dd960.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRenderManager2D_Draw2DTriangle_009ddff0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRenderManager2D_Draw3DScaledText_009dbff0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRenderManager2D_DrawBufferAdds_009dc4c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CRenderManager2D_DrawBufferedPolys_009d9c80.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9d/CRenderManager2D_Render2DDrawList_009da9f0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9d/CSmallVector_CCharString_Clear_009d2110.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/CStateBlockFunctions_Apply_009df060.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/NProfileTimer_EndProfile_009d8250.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/NProfileTimer_StartProfile_009d8240.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/Render2D_DepthComparators_009d8c30.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/STLAsyncBankDisposalList_clear_009d7db0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/STLAsyncBankTree_erase_009d7430.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/STLAsyncBankTree_Minsert_009d7990.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/STLAsyncDisposalList_push_back_009d7b00.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/STLAsyncPointerTree_Distance_009d7550.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/STLAsyncPointerTree_EqualRange_009d7c80.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/STLAsyncPointerTree_InsertUnique_009d7cf0.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/STLAsyncPointerTree_KeyErase_009d7f40.cpp clean ptr_warning C4302,C4311,C4312 +00/9d/STLAsyncPointerTree_RangeErase_009d7760.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CCharString_FastCompare_009e6530.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_AddCommandLineToHistory_009eaf80.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_AddInput_009ec5e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_BindingCommands_009ecb70.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_CConsole_009ecd80.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_ColourCommands_009eaa50.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_CommandLineBuffers_009ea5a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_Destructor_009ecf30.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_GetRegisteredConsoleInput_009eb220.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_GuessMode_009eaff0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_HistoryScroll_009eabe0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_Initialise_009ed190.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_ListCommands_009eb590.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_ModeHelpers_009ea340.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_ProcessInput_009ea530.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_RunScript_009ec890.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_ScrollToHistoryBeginning_009eac80.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_SendMessageToUser_009eb260.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsole_SetHelpBarString_009eb2c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleCommandBase_GetFormattedNameCConsoleCommandBaseMBEAVCCharStr_009ea3a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleCommandBase_GetHelp_009ea7d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleCommandCConsole_CallCommandFunction_009efeb0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleCommandLine_Constructors_009ea2b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleCommandLine_Editing_009ea4b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleCommandParameters_CConsoleCommandParameters_009ebb80.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleCommandParameters_CConsoleCommandParameters_009ebc00.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleCommandParameters_GetParameterTypeAsString_009ea740.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleHistoryList_clear_009ee7b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleHistoryList_insert_009eeb00.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleInputTree_insertunique_009efb80.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleInputTree_Minsert_009ef750.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleVariable_GetFormattedName_009ea490.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleVariable_GetHelp_009ea3c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CConsoleVariable_ProcessCommandLine_009ec430.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CCountedPointer_Reset_009e3090.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CCountedPointer_Reset_009e3100.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CDateAndTime_CDateAndTime_009e9ab0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CDateAndTime_Conversions_009e9a30.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CDateAndTime_FileTimeConstructor_009e9db0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CDateAndTime_Initialise_009e9cb0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CDateAndTime_Misc_009e9c40.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CDriveManager_FileOperations_009e4b70.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CDriveManager_Initialise_009e4ad0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CFontManager_1CFontManager_009e2d00.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CFontManager_CFontManager_009e2e30.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CFontManager_GetFont_009e2c80.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CFontManager_GetFreeVertexBuffer_009e2ce0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CFontManager_GetStaticFont_009e2ba0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CFontManager_GetStreamingFont_009e2c10.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CFontManager_ReleaseFont_009e2b40.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CGenericVar_Lifetime_009ea150.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CJoystick_Clear_009e4940.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CJoystick_ClearAllMaintainedEvents_009e42c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CJoystick_EventScanEnd_009e41c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CJoystick_EventScanNext_009e4280.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CJoystick_EventScanStart_009e4230.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CJoystick_SetExclusiveMode_009e41a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CJoystick_SetHasInputFocus_009e4950.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CJoystick_Update_009e41b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CJoystick_UpdateInsertionsAndRemovals_009e4930.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CMemoryFile_Clone_009e97f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CMemoryFile_Close_009e9390.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CMemoryFile_CMemoryFile_009e97b0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CMemoryFile_Copy_009e9760.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CMemoryFile_CopyConstructor_009e99c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CMemoryFile_DumpToFile_009e9330.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9e/CMemoryFile_MovePosition_009e9140.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CMemoryFile_Open_009e90a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CMemoryFile_Open_009e93c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CMemoryFile_Open_009e94a0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9e/CMemoryFile_OpenFromFile_009e9420.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CMemoryFile_Read_009e9220.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CMemoryFile_SetLength_009e9730.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CMemoryFile_SetPosition_009e90c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CMemoryFile_Write_009e95f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CPersistTraits_TransferOut_009e7f00.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CPersistTraitsSLONG_TransferIn_009e2590.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CPersistTraitsSLONG_TransferOut_009e2560.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CPixelFormat_GetColourDepth_009e3820.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CPixelFormat_Initialise_009e3830.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CPixelFormat_IsSigned_009e3970.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CQuickDrawTriInfoVector_Erase_009e15e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CSymbolMap_AddSymbolDirty_009e5420.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CSymbolMap_Clear_009e5250.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CSymbolMap_CSymbolMap_009e5200.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CSymbolMap_DeallocateUnusedMemory_009e5350.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CSymbolMap_Error_009e4f80.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CSymbolMap_FindFirstSymbolWithVal_009e5090.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CSymbolMap_FindSymbol_009e5170.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CSymbolMap_GetSymbolSLONG_009e5120.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CSymbolMap_LoadFromFile_009e5c90.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CSymbolMap_ParseBuffer_009e5ad0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CSymbolMap_SymbolExists_009e51c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CSymbolMap_UpdateDirty_009e5c30.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CSymbolMap_VectorMemory_009e7680.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CVectorMap_SymbolMap_DeallocateUnusedMemory_009e7c70.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CVectorMap_SymbolMap_LowerBound_009e6910.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/CVersion_009ea080.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/global_CPUAheadCounterCallback_009e2540.cpp clean inline_asm +00/9e/Global_GetProgressDisplay_009ea060.cpp clean error C2821 fable_boot.h:82 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9e/Global_GFABoxToVBox_009e2020.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/Global_GFACoordToVCoord_009e21d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/Global_GFAYToVY_009e1dd0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/Global_GFStringToInt_009e1a80.cpp clean ptr_warning C4267 +00/9e/Global_GFVirtualToActualScalars_009e1e10.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/Global_GFXToY_009e23a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/Global_SetProgressDisplay_009e9fd0.cpp clean error C2821 fable_boot.h:82 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9e/Global_VirtualCoordinateHelpers_009e1d00.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/NProgressDisplay_DisplayProgress_009e9f40.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/NProgressDisplay_IsActive_009e9fb0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/NProgressDisplay_SetToDisplayText_009e9f90.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/NProgressDisplay_StartProgress_009e9ef0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/NProgressDisplay_StopProgress_009e9f20.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_AdjustHeap_SymbolEntry_009e6db0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_InsertionSort_SymbolEntry_009e7590.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_IntrosortLoop_SymbolEntry_009e7d30.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_LinearInsert_SymbolEntry_009e6fa0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_MakeHeap_SymbolEntry_009e7100.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_PartialSort_SymbolEntry_009e7b40.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_Partition_SymbolEntry_009e6c80.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_PopHeap_SymbolEntry_009e7060.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_PushHeap_SymbolEntry_009e67c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_Sort_SymbolEntry_009e7e40.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_SortHeap_SymbolEntry_009e7950.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_Swap_SymbolEntry_009e6480.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_UnguardedInsertionSort_SymbolEntry_009e6d10.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_UnguardedLinearInsert_SymbolEntry_009e66f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_UninitializedCopy_SymbolEntry_009e6270.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STL_UninitializedFillN_SymbolEntry_009e62e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STLConsoleInputTree_Lookups_009eec10.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STLConsoleParameters_Minsertoverflow_009ef900.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STLQuickDrawInfo_Clear_009e1650.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STLQuickDrawInfo_InsertOverflow_009e1750.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STLQuickDrawInfo_UninitializedCopy_009e11a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STLVector_SymbolEntry_M_insert_overflow_009e77e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STLVector_SymbolEntry_M_range_insert_009e73f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STLVector_SymbolEntry_push_back_009e7ab0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STLVector_SymbolEntry_reserve_009e72c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9e/STLVector_SymbolInfo_M_insert_overflow_009e76f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/009f9ee0.pre.cpp error C2065 error C2065 009f9ee0.pre.cpp:67 C2065 'CPixelFormatByteLengthView': undeclared identifier +00/9f/CCodeSectionManager_CCodeSectionManager_009f0320.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CCodeSectionManager_GetSectionDesc_009f0240.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CCodeSectionManager_LoadSection_009f02c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CCodeSectionTree_find_009f0d90.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CFrontEndScreen_AddActionOnLeftClicked_009fd870.cpp clean inline_asm +00/9f/CGameEventPackageSet_AddPackage_009f16f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGameEventPackageSet_Clear_009f1720.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGameEventPackageSet_CompressIntoBuffer_009f19a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGameEventPackageSet_Ctor_009f1a80.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGameEventPackageSet_GetNoPackages_009f1750.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGameEventPackageSet_GetPackage_009f1730.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicBankManager_CGraphicBankManager_009f8690.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicDataBank_SceneDelay_009fd0c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfo_UnloadFrame_009fe8e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_BeginLoading_009fe620.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_CFrameData_Constructor_009fe380.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_CFrameData_Destructor_009fe6c0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_CFrameData_FreeTextureMemory_009fe460.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_CFrameData_GetDebugMemoryInUse_009fdcb0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_CFrameData_GetUnloadableMemory_009fd830.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_CFrameData_IsResourceLoaded_009fd7f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_CFrameData_OnResourceLoaded_009ff860.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_CFrameData_ReleaseTexture_009fea00.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_CFrameData_ResetDebugMemoryInUse_009fd4d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_CFrameData_ResetTexture_009fe440.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_CFrameData_Sizeof_009fdbe0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_CFrameData_StopLoading_009fdc20.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_CFrameData_UploadFrame_009ff220.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CGraphicInfoFrame_InitialiseFrame_009fe550.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CInputManager_ClearEvents_009f5540.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9f/CInputManager_EventScanEnd_009f4ac0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CInputManager_EventScanNext_009f4f10.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CInputManager_EventScanStart_009f4ed0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CInputManager_IsJoystickDeviceNumberActive_009f5250.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9f/CInputManager_SetExclusiveMode_009f4e70.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9f/CInputManager_SetHasInputFocus_009f4e20.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9f/CInputManager_StreamInputsFromFile_009f5e10.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CInputManager_StreamInputsToFile_009f4cf0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CInputManager_UpdateChecksum_009f4a90.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CMemoryAllocator_Aligned_009f2be0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_AllocateDebug_009f2b90.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_ClearPerformanceStats_009f26b0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_ConsolidateFreeAreas_009f2660.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_Constructor_009f20b0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_Create_009f2870.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_Destructor_009f2310.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_GetAllocationSize_009f2390.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_GetNoAllocatedAreas_009f2550.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_GetNoAllocations_009f1f10.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_GetTotalMemoryAllocated_009f2450.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_InitDebugInfo_009f28d0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_InternalAllocate_009f2a40.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_InternalAllocateDebug_009f2ac0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_InternalDeallocate_009f2920.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_InternalValidateArea_009f1f40.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_NewFrame_009f26d0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_OnFailedAllocation_009f1f00.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CMemoryAllocator_OutOfLineSupport_009f1f90.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/CNavSwitchableLeafNode_GetRegion_009f4980.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CProfile_0CProfile_009f9450.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CProfileManager_0CProfileManager_009f9160.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CResourceBank_AddResource_009fc380.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CResourceBank_AddToLoadingQueue_009fc3d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CResourceBank_Lifetime_009fc5f0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CResourceBank_ProcessLoadingQueue_009fc410.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CResourceList_FrameEnd_009fc150.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CResourceList_RemoveResource_009fc020.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CRoughFrameCounter_CRoughFrameCounter_009f8b10.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CRoughFrameCounter_NewFrame_009f8ba0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CScratchBuffer_Initialise_009f4910.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CScratchBuffer_Uninitialise_009f4950.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CSurface_CanDoProperSignedConversion_009f35d0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CSurface_CopyFromSurface_009f37e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CSurface_GetDimensions_009f2f90.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CSurface_GetPixelFormat_009f2f60.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CSurface_InitialiseAsDepthBuffer_009f32a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CSurface_InitialiseAsImage_009f3230.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CSurface_InitialiseAsRenderTarget_009f3310.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CSurface_InitialiseFromPreallocatedSurface_009f2ec0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CTexture_CopyFromTexture_009fa4e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CTexture_Initialise_009fa280.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CVolume_Destructor_009fccf0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CVsyncCallbackTimer_ClearAllQueries_009f7250.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CVsyncCallbackTimer_Constructor_009f70e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CVsyncCallbackTimer_Destructor_009f7330.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CVsyncCallbackTimer_GetInfo_009f7050.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CVsyncCallbackTimer_OnPostSwapScreens_009f73c0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/9f/CVsyncCallbackTimer_OnPreDeviceReset_009f73a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CVsyncPendingQueryList_Clear_009f7830.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CVsyncPendingQueryList_NodeOperations_009f7ad0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/CXMVPlaybackInfo_CXMVPlaybackInfo_009f49a0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/MemoryAllocatorShutdown_009f2d20.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/9f/STLInputEventVector_erase_009f67e0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/STLProfileChildrenVector_0vector_009f9930.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/STLProfileTreeList_insert_009f9ba0.cpp clean ptr_warning C4302,C4311,C4312 +00/9f/STLProfileTreeList_insert_009f9c40.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CGraphicInfoFrame_ReleaseData_00a00680.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CHandleBasedMemoryPool_Areas_00a01d20.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CHandleBasedMemoryPool_CanMoveResource_00a03a40.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CHandleBasedMemoryPool_CombineAreas_00a01e40.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CHandleBasedMemoryPool_Free_00a02b30.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a0/CHandleBasedMemoryPool_Growable_00a02020.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a0/CHandleBasedMemoryPool_InsertFreeArea_00a01da0.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CHandleBasedMemoryPool_InternalSetResource_00a028b0.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CHandleBasedMemoryPool_Lock_00a026f0.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CHandleBasedMemoryPool_MoveResource_00a02120.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CHandleBasedMemoryPool_ValidateResource_00a03a50.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CInputEvent_0CInputEvent_00a04410.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CInputEvent_1CInputEvent_00a03b30.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CInputEvent_GetChar_00a03b80.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CInputEvent_GetDeviceNumber_00a03b60.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CInputEvent_GetJoystickButton_00a03b90.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CInputEvent_GetKey_00a03b70.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CInputEvent_GetMouseWheelMovement_00a03bb0.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CInputEvent_Getters_00a043d0.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CInputEvent_GetType_00a03b40.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CInputEvent_Setters_00a03bc0.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CMeshBankManager_CMeshBankManager_00a0a110.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CMeshBankManager_OnPostDeviceReset_00a09d80.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CMeshBankManager_OnPreDeviceReset_00a09d90.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CRenderManagerCore_DeviceLifetime_00a0a940.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CRenderManagerCore_DrawCurrentBuffersIndexed_00a0aab0.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CRenderManagerCore_GetCurrentAWindow_00a0aaa0.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CRenderManagerCore_IndexedTransforms_00a0b1e0.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CRenderManagerCore_MatrixSetters_00a0b0a0.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CRenderManagerCore_SetAWindow_00a0aa80.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/CRenderManagerCore_SetVWindow_00a0b560.cpp clean ptr_warning C4302,C4311,C4312 +00/a0/Global_FableViewportE2_00a0aac0.cpp clean inline_asm +00/a1/CRenderTarget_Construct_00a10dc0.cpp clean ptr_warning C4302,C4311,C4312 +00/a2/CMeshDataBank_MeshGetters_00a24280.cpp clean ptr_warning C4302,C4311,C4312 +00/a2/CMeshDataBank_PeekAnimation_00a242c0.cpp clean ptr_warning C4302,C4311,C4312 +00/a2/CMeshDataBank_PrepareDataForInclusionCMeshDataBankMAEXKABVCArr_00a24410.cpp clean ptr_warning C4302,C4311,C4312 +00/a3/ConsvalstdallocatorstdpairCWideStringconstfloatstdpairCWideStringconstfloatstd_pairCWideStringfloat_00a3fb90.cpp clean error C2821 ConsvalstdallocatorstdpairCWideStringconstfloatstdpairCWideStringconstfloatstd_pairCWideStringfloat_00a3fb90.cpp:4 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a3/CVideoSys_GetTexture_00a3b320.cpp clean ptr_warning C4302,C4311,C4312 +00/a3/CVideoSys_Pause_00a3b1c0.cpp clean ptr_warning C4302,C4311,C4312 +00/a3/CVideoSys_Stop_00a3b1f0.cpp clean ptr_warning C4302,C4311,C4312 +00/a3/CVideoSys_WaitForState_00a3b0f0.cpp clean ptr_warning C4302,C4311,C4312 +00/a4/CLipSyncDataBank_GetEnumerationType_00a427e0.cpp clean inline_asm +00/a5/CShaderResource_Bindings_00a5d4d0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CShaderResource_ConstructionAndClose_00a5d870.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CShaderResource_GetVertexShaderIndex_00a5d510.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CShaderResource_GetVertexShaderName_00a5d5f0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CShaderResource_OpenShaderBank_00a5d7c0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CShaderResource_PointerFillInsert_00a5e3b0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CShaderResource_PointerResize_00a5e6a0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CShaderResource_SetVertexShaderInput_00a5d8b0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CSimpleMemoryPool_BlockLifetime_00a5c890.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a5/CStreamingFontDataBank_1CStreamingFontDataBank_00a5f3a0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBank_AdjustUpdateEntries_00a5eea0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBank_Close_00a5f420.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBank_CloseFonts_00a5f2f0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBank_CStreamingFontDataBank_00a5f290.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBank_GetDataFormatVersion_00a5ee90.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBank_GetEnumerationType_00a5ef10.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBank_GetFontInfo_00a5f090.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBank_GetFontInfo_00a5f0a0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBank_GetLastModifiedTime_00a5f9d0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBank_InitStreamingEntry_00a5ef50.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBank_IsDiskFile_00a5f9c0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBank_NotifyEntryRemoved_00a5f0b0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBank_PrepareDataForInclusion_00a5f0f0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBankEntry_AcquireFont_00a5f1c0.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/CStreamingFontDataBankEntry_CanUnload_00a5f230.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/Global_GFGetLastWindowsError_00a5ed60.cpp clean ptr_warning C4302,C4311,C4312 +00/a5/global_GFInitVectorMath_00a5b850.cpp clean inline_asm +00/a6/CArray_ManagedTextureData_DeallocateMemory_00a6be40.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CBusyResourceManager_OnPostSwapScreens_00a618d0.cpp clean ptr_warning C4267 +00/a6/CChecksumCacheFile_CChecksumCacheFile_00a60c90.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CChecksumCacheFile_Close_00a60c30.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CChecksumCacheFile_Destructor_00a60ce0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CChecksumCacheFile_GetChecksum_00a60fb0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CChecksumCacheFile_Open_00a60d40.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CChecksumCacheFile_OpenConstructor_00a61150.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CChecksumCacheFile_Save_00a60aa0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBank_1CFontDataBank_00a63b30.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBank_CFontDataBank_00a63630.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBank_Close_00a635c0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBank_CloseFonts_00a633a0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBank_GetDataFormatVersion_00a63340.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBank_GetEnumerationType_00a63380.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBank_GetFontInfo_00a635d0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBank_GetFontInfo_00a635e0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBank_GetLastModifiedTime_00a63ad0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBank_IsDiskFile_00a63ac0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBank_LoadFont_00a63350.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBank_NotifyEntryAdded_00a636c0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBank_NotifyEntryRemoved_00a635f0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBank_PrepareDataForInclusion_00a63420.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBankEntry_AcquireFont_00a634f0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBankEntry_CanUnload_00a63560.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CFontDataBankEntry_Unload_00a63580.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CGenericVarRef_PrintToString_00a63c00.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CKeyboard_ClearAllMaintainedEvents_00a66860.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CKeyboard_GetKeyName_00a66ad0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CKeyboard_SetHasInputFocus_00a66ac0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CMemoryAllocatorBase_ConsolidateFreeAreas_00a65b50.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorBase_Constructor_00a65ad0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorBase_OperatorDelete_00a66750.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorBase_OperatorNew_00a66730.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_Allocation_00a662a0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_ChunkLifetime_00a66160.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_ChunkLists_00a65e70.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_ConsolidateFreeAreas_00a65d30.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_Constructor_00a65ff0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_Destructor_00a65d20.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_Enumeration_00a660a0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_GetAllocationSize_00a65d40.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_GetAllocationSizeIncludingHeader_00a65de0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_GetFragmentation_00a66550.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_GetHeaderOverhead_00a65d70.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_GetLargestFreeAreaSize_00a65df0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_GetNoAllocatedAreas_00a65e50.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_GetNoFreeAreas_00a65e20.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_GetTotalMemoryAllocated_00a65da0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_GetTotalMemoryControlledByAllocator_00a65d50.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_NewChunk_00a65f40.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorFixedSize_Validate_00a66260.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_Allocate_00a659c0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_AllocateMemoryFromExistingChunks_00a656e0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_AllocationSizes_00a651b0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_Constructor_00a65930.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_Counts_00a64530.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_Deallocate_00a65a50.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_DeleteChunk_00a65390.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_Destructor_00a65690.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_Enumeration_00a65260.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_GetFragmentation_00a64590.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_GetLargestFreeAreaSize_00a64500.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_InsertFixup_00a64740.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_InsertIntoTree_00a64e70.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_NewChunk_00a65720.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_NewChunkDemandPaged_00a65830.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_RemoveFromTree_00a64ef0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_RemoveFromTreeFixup_00a64910.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_Totals_00a64db0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_TreeQueries_00a64c10.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMemoryAllocatorVariableSize_Validate_00a65330.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CMouse_ClearAllMaintainedEvents_00a66c00.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CMouse_SetHasInputFocus_00a673a0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CMovableResourceMemoryPool_CanMoveResource_00a62f70.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CMovableResourceMemoryPool_FreeResource_00a62ed0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CMovableResourceMemoryPool_MoveResource_00a62f30.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CMovableResourceMemoryPool_ValidateResource_00a62ec0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CPreallocTexturePool_Allocate_00a69830.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CPreallocTexturePool_Frame_00a696b0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CPreallocTexturePool_Uninitialise_00a6a4d0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CPreallocTexturePool_Usage_00a69ea0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CShaderBankEntry_Constructor_00a62120.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CShaderDataBank_Cleanup_00a62050.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CShaderDataBank_GetVertexShader_00a61fa0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CShaderDataBank_Lifetime_00a62020.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CShaderDataBank_NotifyEntryAdded_00a623d0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CTextureManager_Allocate_00a69b70.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CTextureManager_CalculateTextureSize_00a698f0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CTextureManager_GetPoolIndex_00a69730.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CTextureManager_PoolAccess_00a696f0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CTextureManager_PoolUsage_00a69f10.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CTextureManager_ReduceMipmapLevel_00a69f70.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CTextureManager_ResizePoolTextureCount_00a6abe0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CTextureManager_Uninitialise_00a696d0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CVertexBufferWin32_Initialise_00a63150.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CVertexBufferWin32_Uninitialise_00a63200.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CVertexShader_Lifetime_00a62fb0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CVolume_InitialiseFromVolumeTextureMipmap_00a67770.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CVolumeTexture_CreateTexture_00a68250.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CVolumeTexture_FileWrappers_00a694e0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CVolumeTexture_Lock_00a685e0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/CVolumeTexture_Sizeof_00a67f30.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/CVolumeTexture_Uninitialise_00a68060.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/STLChecksumCacheMap_Index_00a617e0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/STLChecksumCacheTree_erase_00a60a00.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/STLChecksumCacheTree_Find_00a608f0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/STLChecksumCacheTree_InsertHint_00a614b0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/STLChecksumCacheTree_InsertNode_00a61270.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/STLChecksumCacheTree_InsertUnique_00a61360.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/STLChecksumCacheTree_LowerBound_00a609c0.cpp clean ptr_warning C4302,C4311,C4312 +00/a6/VariableAllocatorChunk_AllocAreaFromArea_00a65480.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/VariableAllocatorChunk_FreeArea_00a658e0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/VariableAllocatorChunk_Init_00a65410.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/VariableAllocatorChunk_InsertAreaIntoFreeList_00a650f0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a6/VariableAllocatorChunk_RemoveAreaFromFreeList_00a65060.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/a7/CNavQuadTree_InitialiseLines_00a7a5e0.cpp error C2664 error C2664 CNavQuadTree_InitialiseLines_00a7a5e0.cpp:20 C2664 'void CNavQuadTree::UpdateLines(CNavLineCellVector &,const std::vector> &,const C2DBox +00/a7/CNavQuadTree_IsAreaBlockedByLines_00a76f30.cpp clean inline_asm +00/a8/global_VectorConstructInitializedC2DLineF_00a80360.cpp clean inline_asm +00/a9/C3DAnimFileChunks_00a98a70.cpp clean ptr_warning C4302,C4311,C4312 +00/a9/vectorEWeaponClass_UmoveEWeaponClass_00a98620.cpp clean ptr_warning C4302,C4311 +00/aa/C3DAnimFileMovementVectorChunk_Methods_00aae0f0.cpp clean ptr_warning C4302,C4311,C4312 +00/aa/C3DAnimFileSequenceChunk_Methods_00aaf5b0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/aa/C3DAnimFileTimingEventChunk_Methods_00aae190.cpp clean ptr_warning C4302,C4311,C4312 +00/aa/C3DAnimFileXSequenceChunk_Methods_00aa4650.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/aa/C3DFileContainerChunks_00aa9670.cpp clean ptr_warning C4302,C4311,C4312 +00/aa/C3DMeshFileConvexVolumeChunk_Methods_00aa9ad0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/aa/C3DMeshFileDummyObjectChunk_Methods_00aa9910.cpp clean ptr_warning C4302,C4311,C4312 +00/aa/C3DMeshFileHelperPointChunk_Methods_00aa96b0.cpp clean ptr_warning C4302,C4311,C4312 +00/aa/C3DMeshFileXChunks_00aa4b50.cpp clean ptr_warning C4302,C4311,C4312 +00/aa/C3DMeshFileXLeafChunks_00aa4af0.cpp clean ptr_warning C4302,C4311,C4312 +00/aa/C3DMeshFileXTriangleStripChunk_Methods_00aa5930.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/aa/C3DMeshFileXUVTangentChunk_Methods_00aa57f0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/aa/C3DMeshFileXVertexDataChunk_Methods_00aa4d20.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/aa/CChunkedFileChunk_Base_00aa75d0.cpp clean ptr_warning C4302,C4311,C4312 +00/ab/C3DAnimFileBoneMaskChunk_Methods_00ab0110.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/ab/C3DAnimFileObjectChunk_Methods_00ab0090.cpp clean ptr_warning C4302,C4311,C4312 +00/ab/C3DMeshFileBoneChunk_Methods_00ab9ea0.cpp clean ptr_warning C4302,C4311,C4312 +00/ab/C3DMeshFileGroupChunk_Methods_00abae50.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/ab/C3DMeshFileMapChunk_Methods_00abfe80.cpp clean ptr_warning C4302,C4311,C4312 +00/ab/C3DMeshFileMaterialChunk_Methods_00abfbf0.cpp clean ptr_warning C4302,C4311,C4312 +00/ab/C3DMeshFileMaterialExtChunk_Methods_00abfd70.cpp clean ptr_warning C4302,C4311,C4312 +00/ab/C3DMeshFilePrimitiveChunk_Methods_00abc390.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/ab/C3DMeshFileSmoothingGroupsChunk_Methods_00abbfc0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/ab/C3DMeshFileSubMeshChunk_Methods_00ab9af0.cpp clean ptr_warning C4302,C4311,C4312 +00/ab/C3DMeshFileTransformationChunk_Methods_00ab9c30.cpp clean ptr_warning C4302,C4311,C4312 +00/ab/C3DMeshFileTrianglesChunk_Methods_00abbe90.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/ab/C3DMeshFileUniqueVerticesChunk_Methods_00abc040.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/ab/C3DMeshFileVerticesChunk_Methods_00abbd70.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/ab/CVertexShaderInput_GenerateD3DDeclaration_00ab3870.cpp error C2065 error C2065 CVertexShaderInput_GenerateD3DDeclaration_00ab3870.cpp:50 C2065 'stream': undeclared identifier +00/ac/C3DMeshFileClothChunk_Methods_00ac6600.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/ac/C3DMeshFileEngineMaterialChunk_Methods_00ac51d0.cpp clean ptr_warning C4302,C4311,C4312 +00/ac/C3DMeshFilePartitionChunk_Create_00ac29c0.cpp clean ptr_warning C4302,C4311,C4312 +00/ae/CParticleEmitterDatabase_Ctor_00ae8bf0.cpp clean ptr_warning C4302,C4311,C4312 +00/ae/CParticleProgram_RelocateData_00ae04c0.cpp clean ptr_warning C4302,C4311 +00/ae/CPSCManager_Destructor_00aeca80.cpp clean ptr_warning C4302,C4311,C4312 +00/ae/NParticleEngine_GetParticleEmitterDatabase_00aeaae0.cpp clean ptr_warning C4302,C4311,C4312 +00/ae/NParticleEngine_Initialise_00aebe20.cpp clean ptr_warning C4302,C4311,C4312 +00/ae/NParticleEngine_SetEnvironmentInteraction_00aeab20.cpp clean ptr_warning C4302,C4311,C4312 +00/ae/NParticleEngine_Uninitialise_00aebea0.cpp clean ptr_warning C4302,C4311,C4312 +00/ae/NParticleEngineCParticleEmitterDatabase_OpenBank_00ae8980.cpp clean ptr_warning C4302,C4311,C4312 +00/ae/ParticleBankSetters_00aeaa80.cpp clean ptr_warning C4302,C4311,C4312 +00/ae/ParticleManagerPointers_Reset_00aecac0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_BackendDefaults_00b23cf0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_CameraSetters_00b23b50.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_ChangeCameraAngle_00b23a50.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_ClipBox_00b242a0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_ClipSphere_00b24150.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_Constructor_00b260b0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_CutsceneAddCameraAngle_00b27d50.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_Destructor_00b261d0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_DeviceReset_00b25d00.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_EffectControls_00b23c00.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_GetPrimitiveDrawDistanceFactor_00b23d00.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_GlobalColourPeeks_00b25d20.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_LightingSetters_00b23ea0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_MorphForwarders_00b239c0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_PrimitiveDispatch_00b23bb0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_PrimitiveTextureForwarders_00b23be0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_RenderColorFilteredQuad_00b25c00.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_RenderFrame_00b25950.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_ResourceGetters_00b25c80.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_SceneControls_00b239a0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_SetAttachedPrimitive_00b23c80.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_SetEnvironmentalEffectsAlwaysUpdate_00b23b90.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_SetVideoOptions_00b24690.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_ShadowBufferForwarders_00b23f70.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_SmartRestoreComponentVideoMemory_00b24500.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_Uninitialise_00b25d80.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_WeatherOverrides_00b24010.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_WorldFrames_00b24030.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngine_WorldMapForwarders_00b23db0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngineCamera_RefreshD3DTransforms_00b2fc50.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngineLayerRenderer_Render_00b2ab80.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngineSurfaceCompositio_LoadNewScene_00b2aee0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngineSurfaceCompositionManager_ChangeSceneForTextureBank_00b2ad80.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CEngineSurfaceCompositionManager_PeekSceneFilterFlags_00b2f5c0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CIEngine_Lifetime_00b232d0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/CTextureManager_GetPoolTextureSize_00b244f0.cpp clean ptr_warning C4302,C4311,C4312 +00/b2/Global_GFAllocEngine_00b26340.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CBankFileAsync_SetLoadingMemoryPool_00b3e820.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEngineInternalPrimitiveBase_IsInsideFrustum_00b3aa10.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEngineInternalPrimitiveBase_WasRendered_00b3aaa0.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEngineMap_CloseStaticMap_00b3ef40.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEngineMap_OnGameMapUnloaded_00b3edc0.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEngineMap_ReadMapInfoBlock_00b3efa0.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEnginePrimitiveRenderer_AddPrimitive_00b32e90.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEnginePrimitiveRenderer_Children_00b31ef0.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEnginePrimitiveRenderer_FrameEnd_00b32820.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEnginePrimitiveRenderer_InitialiseGlobalLights_00b32300.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEnginePrimitiveRenderer_PostRenderLayer_00b32230.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEnginePrimitiveRenderer_PreRenderLayer_00b32ad0.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEnginePrimitiveRenderer_PrimitiveSetters_00b31e90.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEnginePrimitiveRenderer_Render_00b33010.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEnginePrimitiveRenderer_ResetWorldTransform_00b32100.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEnginePrimitiveRenderer_SetDrawDistanceQuality_00b32010.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEnginePrimitiveRenderer_SetPersistentPrimitive_00b324a0.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEngineResourceManager_ExternalTexture_00b3b1f0.cpp clean layout rebuild_abi.h rebuild_abi.h:38 C2118 negative subscript +00/b3/CEngineResourceManager_ResizeHiresTextureMemory_00b3b4f0.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEngineWorldMap_LinkMapToWorld_00b3edf0.cpp clean ptr_warning C4302,C4311,C4312 +00/b3/CEngineWorldMap_UnlinkMapFromWorld_00b3ee80.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineLightingManager_DisablePointLights_00b44d50.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineLightingManager_EnableFog_00b46890.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineMap_Constructor_00b429e0.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineMap_Destructor_00b42a90.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineMap_OpenDevelopmentStaticMap_00b42530.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineMap_OpenRetailStaticMap_00b41e50.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineMap_SetMapVisibility_00b41db0.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEnginePrimitiveRenderer2D_AddPrimitive_00b4a670.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineShadowRenderer_InitialiseTextures_00b4f8e0.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineStaticMapFileBlockManager_Destructor_00b443b0.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineWorldMap_AddMap_00b42d10.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineWorldMap_AutoBuildWorldMapGrid_00b40070.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineWorldMap_CloseStaticMapFile_00b40000.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineWorldMap_DeleteAllMaps_00b42c70.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineWorldMap_DeleteMap_00b42b10.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineWorldMap_LoadWaterData_00b41fa0.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineWorldMap_OnGameMapLoaded_00b401e0.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineWorldMap_OnGameMapUnloaded_00b40230.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineWorldMap_OpenStaticMaps_00b42750.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineWorldMap_OpenStaticMapsUsingCommonRetailHeader_00b420f0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/b4/CEngineWorldMap_SetStaticMapFileForUse_00b428e0.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/CEngineWorldMap_SetWorld_00b42ed0.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +00/b4/CEngineWorldMap_Visibility_00b42390.cpp clean ptr_warning C4302,C4311,C4312 +00/b4/StaticMapBlockTree_Erase_00b441f0.cpp clean ptr_warning C4302,C4311,C4312 +00/b5/CEngineComponent_Lifetime_00b59710.cpp clean ptr_warning C4302,C4311,C4312 +00/b5/CEngineScreenEffectGlowRenderer_Quality_00b5ee00.cpp clean ptr_warning C4302,C4311,C4312 +00/b5/CEngineScreenEffectRadialBlurRenderer_Quality_00b5bfd0.cpp clean ptr_warning C4302,C4311,C4312 +00/b5/CEngineWeatherRenderer_AddWeatherMask_00b50c80.cpp error C2039 error C2039 CEngineWeatherRenderer_AddWeatherMask_00b50c80.cpp:3 C2039 'AddWeatherMask': is not a member of 'CEngineWeatherRenderer' +00/b5/CEngineWeatherRenderer_ClipLockedBoxToCamera_00b52120.cpp error C2039 error C2039 CEngineWeatherRenderer_ClipLockedBoxToCamera_00b52120.cpp:3 C2039 'ClipLockedBoxToCamera': is not a member of 'CEngineWeatherRenderer' +00/b6/CEngineLandscapeRenderer_OnReleaseDefaultPoolResources_00b676a0.cpp clean ptr_warning C4302,C4311 +00/b6/CEngineLandscapeRenderer_PeekSceneFilterFlags_00b6ca10.cpp clean ptr_warning C4302,C4311,C4312 +00/b6/CEngineWaterRenderer_CloseStaticMap_00b6db80.cpp clean ptr_warning C4302,C4311,C4312 +00/b6/CEngineWaterRenderer_LoadNonPatchData_00b6daf0.cpp clean ptr_warning C4302,C4311,C4312 +00/b6/CEngineWaterRenderer_OpenStaticMapForUse_00b6d4d0.cpp clean ptr_warning C4302,C4311,C4312 +00/b6/MapRenderers_CreateMap_00b67140.cpp clean ptr_warning C4302,C4311,C4312 +00/b6/MapRenderers_DeleteMap_00b67180.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/CEngineInternalPrimitiveBase_ClearAllChildPrimitivesCEngineIntern_00b8fde0.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/CEngineInternalPrimitiveBase_Lists_00b8fdf0.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/CEngineInternalPrimitiveBase_SetFadeDistanceWithGamePrimitiveDefaults_00b8fd40.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/CEngineScreenEffectDisplacementRenderer_GetConsoleEnableFunctionNameCEngineScreenEffectD_00b88f80.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/CEngineScreenEffectDisplacementRenderer_Quality_00b86b00.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/CEngineSubPrimitiveRenderer_DeleteList_00b84400.cpp clean ptr_warning C4267 +00/b8/CEngineSubPrimitiveRenderer_GetCount_00b842b0.cpp clean ptr_warning C4267 +00/b8/CEngineSubPrimitiveRenderer_RadixSort_00b847d0.cpp error C2065 error C2065 CEngineSubPrimitiveRenderer_RadixSort_00b847d0.cpp:16 C2065 'i': undeclared identifier +00/b8/CEngineSubPrimitiveRenderer_RenderList_00b842d0.cpp clean ptr_warning C4267 +00/b8/CEngineSubPrimitiveRenderer_RenderListTwoPass_00b84320.cpp clean ptr_warning C4267 +00/b8/CEngineSubPrimitiveRenderer_RenderType_00b849f0.cpp clean ptr_warning C4267,C4302,C4311,C4312 +00/b8/CEngineSubPrimitiveRenderer_SortList_00b84990.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/CEngineSubPrimitiveRenderer_SortSingleRadix_00b84b20.cpp error C2065 error C2065 CEngineSubPrimitiveRenderer_SortSingleRadix_00b84b20.cpp:8 C2065 'i': undeclared identifier +00/b8/SubPrimitiveSort_AdjustHeap_00b852f0.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_DefaultSort_00b868c0.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_InplaceStableSort_00b86570.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_InsertionSort_00b85b30.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_Introsort_00b867a0.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_LinearInsert_00b85870.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_LowerBound_00b85240.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_MakeHeap_00b855f0.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_MergeWithoutBuffer_00b86140.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_PartialSort_00b86450.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_Partition_00b85430.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_PushHeap_00b84fd0.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_Rotate_00b85660.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_SortHeap_00b85e10.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_SwapRanges_00b85390.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_UnguardedInsertionSort_00b851d0.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_UnguardedLinearInsert_00b84f40.cpp clean ptr_warning C4302,C4311,C4312 +00/b8/SubPrimitiveSort_UpperBound_00b85280.cpp clean ptr_warning C4302,C4311,C4312 +00/b9/CEngineInternalPrimitiveStaticMesh_GetWorldPosition_00b9d1e0.cpp error C2039 error C2039 CEngineInternalPrimitiveStaticMesh_GetWorldPosition_00b9d1e0.cpp:3 C2039 'GetWorldPosition': is not a member of 'CEngineInternalPrimitiveStaticMesh' +00/b9/CEngineScene_ClearNonPersistent_00b91980.cpp clean ptr_warning C4302,C4311,C4312 +00/b9/CEngineScene_FrameEnd_00b94a30.cpp clean ptr_warning C4302,C4311,C4312 +00/b9/CEngineScene_InvalidateBoundingBoxesAndDrawDistances_00b93500.cpp clean ptr_warning C4302,C4311,C4312 +00/b9/CEngineScene_ResetStats_00b93df0.cpp clean ptr_warning C4302,C4311,C4312 +00/b9/CEngineScene_UpdateMaps_00b948e0.cpp clean ptr_warning C4302,C4311,C4312 +00/b9/CEngineScene_UpdatePosition_00b91840.cpp clean ptr_warning C4302,C4311,C4312 +00/b9/SceneStatsTree_Erase_00b95ee0.cpp clean ptr_warning C4302,C4311,C4312 +00/ba/CEngineInternalPrimitive2DClockSprite_GetRenderLayerMask_00bacec0.cpp clean ptr_warning C4302,C4311,C4312 +00/ba/CEngineInternalPrimitive2DSprite_GetRenderLayerMask_00baceb0.cpp clean ptr_warning C4302,C4311,C4312 +00/ba/CEngineInternalPrimitive2DSprite_Update_00bad8a0.cpp clean ptr_warning C4302,C4311,C4312 +00/ba/CEnginePrimitiveManager2DSprites_Render2DPrimitive_00baced0.cpp clean ptr_warning C4302,C4311,C4312 +00/ba/CEnginePrimitiveManager_AddPrimitive_00bacdd0.cpp clean ptr_warning C4302,C4311,C4312 +00/bb/CEnginePrimitiveManager2DSprites_GetSupportedTypes_00bb1640.cpp clean ptr_warning C4302,C4311,C4312 +00/bb/CEnginePrimitiveManager2DSprites_RenderPrimitive_00bb18d0.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/00bdd0e0.pre.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/00bdf010.pre.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CEngineLandscapeMap_Clear_00bdc450.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CEngineLandscapeMap_CloseStaticMap_00bdc4f0.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CEngineLandscapeMap_Constructor_00bdd2f0.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CEngineLandscapeMap_GameMapNotifications_00bdbfe0.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CEngineLandscapeMap_MovableLifetime_00bdd210.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CEngineLandscapeMap_OnMapVisibilityChanged_00bdc3c0.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CEngineLandscapeMap_OpenStaticMap_00bdd0e0.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CEngineLandscapeMap_ReleaseLoadingPatchTreeBase_00bdc000.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CEngineLandscapeMap_RelocateData_00bdc130.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CEngineLandscapeMap_UpdateBackgroundLOD_00bdc180.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CEngineLandscapeMemoryManager_FreeDisposalList_00bd9ce0.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/bd/CEngineLandscapeMemoryManager_FreeVirtual_00bd9b90.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +00/bd/CLocalDetailCacheMap_CloseStaticMap_00bddd50.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CLocalDetailCacheMap_Constructors_00bdefc0.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CLocalDetailCacheMap_CQuadTreeElement_Clear_00bddc00.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CLocalDetailCacheMap_CQuadTreeElement_UnloadStaticContents_00bde020.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CLocalDetailCacheMap_OnMapVisibilityChanged_00bde160.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CLocalDetailCacheMap_QuadLifetime_00bddfb0.cpp clean ptr_warning C4302,C4311,C4312 +00/bd/CLocalDetailCacheMap_QuadRelocation_00bde0c0.cpp clean ptr_warning C4302,C4311,C4312 +00/be/00bebed0.pre.cpp clean ptr_warning C4302,C4311 +00/be/CEnginePrimitive2DViewportManager_CreateInternalPrimitive_00be2260.cpp clean error C2371 CEnginePrimitive2DViewportManager_CreateInternalPrimitive_00be2260.cpp:7 C2371 'size_t': redefinition; different basic types +00/be/CEnginePrimitiveManagerMeshBase_GetComposedTexture_00bebed0.cpp clean ptr_warning C4302,C4311 +00/be/CEngineStaticMapFileBlockManager_DeleteUnusedBlocks_00be04f0.cpp clean ptr_warning C4302,C4311,C4312 +00/be/CEngineStaticMapFileBlockManager_ReleaseRequest_00be0400.cpp clean ptr_warning C4302,C4311,C4312 +00/be/CEngineStaticMapFileBlockManager_Reset_00be05d0.cpp clean ptr_warning C4302,C4311,C4312 +00/be/CEngineStaticMapFileBlockManager_SetBankFileEntry_00be03a0.cpp clean ptr_warning C4302,C4311,C4312 +00/be/CLocalDetailObjectCollectionType_Load_00be27b0.cpp clean ptr_warning C4302,C4311,C4312 +00/be/ConsvalstdallocatorstdpairunsignedlongCAnimationEntrystdpairunsignedlongCAnimati_pairunsignedlongclassCAnimationEntry_00be5bb0.cpp clean inline_asm +00/be/ConsvalstdallocatorstdpairunsignedlongCAnimationEntrystdpairunsignedlongCAnimati_pairunsignedlongclassCAnimationEntry_00bea7e0.cpp clean inline_asm +00/be/STLStaticMapBlockTree_find_00be08e0.cpp clean ptr_warning C4302,C4311,C4312 +00/bf/00bf4130.pre.cpp clean ptr_warning C4312 +00/bf/CEngineLandscapePatch_LoadTextures_00bf4130.cpp clean ptr_warning C4312 +00/bf/CEngineLandscapePatch_RelocateData_00bf3980.cpp clean inline_asm +00/bf/CEngineLandscapeRenderer_GetForegroundBumpShadowedVertexShader_00bf5cd0.cpp clean inline_asm +00/bf/CEngineLandscapeRenderer_GetForegroundShadowedVertexShader_00bf5ca0.cpp clean inline_asm +00/bf/CEngineLandscapeRenderer_GetForegroundSpotLightVertexShader_00bf5c60.cpp clean inline_asm +00/bf/CEngineLandscapeRenderer_GetForegroundSpotShadowedVertexShader_00bf5d00.cpp clean inline_asm +00/bf/CEngineLandscapeRenderer_GetForegroundVertexShader_00bf5c20.cpp clean inline_asm +00/bf/CEngineTexturePalette_Constructor_00bf90a0.cpp clean ptr_warning C4302,C4311,C4312 +00/bf/CEngineTexturePalette_Load_00bf9290.cpp clean ptr_warning C4302,C4311,C4312 +00/bf/CEngineTexturePalette_VectorResize_00bf97a0.cpp clean ptr_warning C4302,C4311,C4312 +00/bf/ConsvalstdallocatorstdpairunsignedlongCAnimationEntrystdpairunsignedlongCAnimati_pairunsignedlongclassCAnimationEntry_00bf36c0.cpp clean inline_asm +00/bf/ConsvalstdallocatorstdpairunsignedlongCAnimationEntrystdpairunsignedlongCAnimati_pairunsignedlongclassCAnimationEntry_00bf6260.cpp clean inline_asm +00/bf/ConsvalstdallocatorstdpairunsignedlongCAnimationEntrystdpairunsignedlongCAnimati_pairunsignedlongclassCAnimationEntry_00bf62e0.cpp clean inline_asm +00/bf/ConsvalstdallocatorstdpairunsignedlongCAnimationEntrystdpairunsignedlongCAnimati_pairunsignedlongclassCAnimationEntry_00bf9900.cpp clean inline_asm +00/bf/ConsvalstdallocatorstdpairunsignedlongCAnimationEntrystdpairunsignedlongCAnimati_pairunsignedlongclassCAnimationEntry_00bf9980.cpp clean inline_asm +00/bf/ConsvalstdallocatorstdpairunsignedlongCAnimationEntrystdpairunsignedlongCAnimati_pairunsignedlongclassCAnimationEntry_00bf9a00.cpp clean inline_asm +00/bf/ConsvalstdallocatorstdpairunsignedlongCAnimationEntrystdpairunsignedlongCAnimati_pairunsignedlongclassCAnimationEntry_00bfd160.cpp clean inline_asm +00/bf/ConsvalstdallocatorstdpairunsignedlongCAnimationEntrystdpairunsignedlongCAnimati_pairunsignedlongclassCAnimationEntry_00bfdcc0.cpp clean inline_asm +00/bf/global_ArrayUnwind_00bfee7a.cpp clean inline_asm +00/bf/global_ehvectordestructoriterator_00bfeed8.cpp clean inline_asm +00/bf/global_securitycheckcookie_00bfe9f9.cpp clean inline_asm +00/c0/CAIBrain_GetCurrentStateGroupDebugText_00c0e6e0.cpp clean inline_asm +00/c0/ConsvalstdallocatorstdpairCCharStringCCharStringstdpairCCharStringCCharStringstd_pairclassCCharStringclassCCharString_00c0bcc0.cpp clean inline_asm +00/c2/CTCInventoryAbilities_AddLearnedExpression_00c21f30.cpp clean ptr_warning C4312 +00/c3/global_IsOverrideableByActionOfSamePriority_00c35ea0.cpp clean ptr_warning C4302,C4311 +00/c4/CTCCreatureNavigation_IsNewDestinationGoingToResetNavigation_00c43370.cpp clean inline_asm +00/c5/global_CPUAheadCounterCallback_00c52a80.cpp clean inline_asm +00/c6/CCreatureActionBase_GetFramesRemaining_00c61be0.cpp clean inline_asm +00/c6/CSimpleMemoryPool_SetCapacity_00c6ad70.cpp clean ptr_warning C4302,C4311 +00/c6/global_FtoL_00c6b240.cpp clean inline_asm +00/c8/global_BatchInvokeWithStackArgsc8c730_00c8c730.cpp clean inline_asm +00/c8/global_FtoL_00c89be0.cpp clean inline_asm +00/c8/std_DestvalCFlasha_00c8db10.cpp clean inline_asm +00/c9/CDefinitionManager_GetPDefFromClassIndex_00c96430.cpp clean ptr_warning C4312 +00/ca/global_FtoL_00ca3b10.cpp clean inline_asm +00/f3/CFrontEndScreen_dtor_00f35b40.cpp clean inline_asm +00/f3/CGPUScratchBufferTextureManager_FreeMemoryPage_00f37030.cpp clean ptr_warning C4302,C4311,C4312 +00/f3/CGPUScratchBufferTextureManager_FreeResourceEntry_00f36f40.cpp clean ptr_warning C4302,C4311,C4312 +01/21/Global_EngineManager_0121df10.cpp clean ptr_warning C4302,C4311,C4312 +01/21/Global_ProgressTipText_0121a3e0.cpp clean ptr_warning C4302,C4311,C4312 +01/21/Global_ProgressTipTitle_0121a400.cpp clean ptr_warning C4302,C4311,C4312 +01/22/Global_2DShaderConstantLayout_01224140.cpp clean ptr_warning C4302,C4311,C4312 +01/22/Global_CommonStaticMapHeaderEntryName_012246c0.cpp clean ptr_warning C4302,C4311,C4312 +01/22/Global_ProgressDisplay_012241b0.cpp clean ptr_warning C4302,C4311,C4312 +01/22/Global_SeaStaticMapBankFileName_01224680.cpp clean ptr_warning C4302,C4311,C4312 +01/22/Global_ShaderRenderManager_01223d00.cpp clean ptr_warning C4302,C4311,C4312 +01/22/Global_WaterStaticMapBankFileName_012246a0.cpp clean ptr_warning C4302,C4311,C4312 +01/37/CMainGameComponent_StaticData_01375550.cpp clean ptr_warning C4302,C4311,C4312 +01/37/DisplayEngine_FadeSettings_01375cdc.cpp clean ptr_warning C4302,C4311,C4312 +01/37/EngineInit_Defaults_01375490.cpp clean ptr_warning C4302,C4311,C4312 +01/37/Global_UnpauseAfterHeroInfo_0137d76c.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +01/37/StartupInitializedScalars_01375444.cpp clean ptr_warning C4302,C4311,C4312 +01/38/NativeDisplayRuntimeData_0138e0b8.cpp clean ptr_warning C4302,C4311,C4312 +01/39/CRenderManager2D_StateBlockLibrary_01396510.cpp clean ptr_warning C4302,C4311,C4312 +01/39/CResourceBank_ResourceAbortingEnabled_01396c5c.cpp clean ptr_warning C4302,C4311,C4312 +01/39/MemoryAllocatorData_01396868.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +01/3b/CAFile_CallbackData_013bc9fc.cpp clean ptr_warning C4302,C4311,C4312 +01/3b/CAFileCurrentPath_013bca10.cpp clean ptr_warning C4302,C4311,C4312 +01/3b/CharStringInstanceCount_013bd800.cpp clean ptr_warning C4302,C4311,C4312 +01/3b/CMainGameComponent_EventDispatch_013b9288.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +01/3b/CompiledResourceFlags_013b8609.cpp clean ptr_warning C4302,C4311,C4312 +01/3b/CPlayerGui_PGuiDef_013b878c.cpp clean ptr_warning C4302,C4311,C4312 +01/3b/CWorld_Frame_013b89bc.cpp layout fable_world.h ptr_warning C4302,C4311,C4312 fable_world.h:125 C2118 negative subscript +01/3b/FullBuildVersion_013b85e0.cpp clean ptr_warning C4302,C4311,C4312 +01/3b/GFMainFrameStatus_013b7d6c.cpp clean ptr_warning C4302,C4311,C4312 +01/3b/Global_PassiveAggressiveMode_013b85f3.cpp clean ptr_warning C4302,C4311,C4312 +01/3b/StartupCacheData_013b837c.cpp clean ptr_warning C4302,C4311,C4312 +01/3b/StartupGraphicsAndRecordingData_013b7c90.cpp clean ptr_warning C4302,C4311,C4312 +01/3b/StartupRecordingFlags_013b85f2.cpp clean ptr_warning C4302,C4311,C4312 +01/3b/WideStringInstanceCount_013bca20.cpp clean ptr_warning C4302,C4311,C4312 +01/3c/AllocatorRangeData_013caaa4.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' +01/3c/ConsoleCommandNameSuffix_013caa48.cpp clean ptr_warning C4302,C4311,C4312 +01/3c/CVsyncCallbackTimer_StaticData_013cb4b0.cpp clean ptr_warning C4302,C4311,C4312 +01/3c/FileInstaller_Data_013ca818.cpp clean error C2821 fable_file_installer.h:51 C2821 first formal parameter to 'operator new' must be 'size_t' +01/3c/Global_BankFileManager_013ca79c.cpp clean ptr_warning C4302,C4311,C4312 +01/3c/Global_DefinitionPath_013ca7dc.cpp clean ptr_warning C4302,C4311,C4312 +01/3c/GSystemManager_013ca618.cpp clean ptr_warning C4302,C4311,C4312 +01/3d/CTextureManager_ReuseBusyTextures_013d28e8.cpp clean ptr_warning C4302,C4311,C4312 +01/3d/ParticleEnvironment_Global_013d2e2c.cpp clean ptr_warning C4302,C4311,C4312 +01/3d/PEmitterDatabase_Global_013d2e14.cpp clean ptr_warning C4302,C4311,C4312 +01/3d/PEmitterManager_Global_013d2e1c.cpp clean ptr_warning C4302,C4311,C4312 +01/3d/PPSCManager_Global_013d2e24.cpp clean ptr_warning C4302,C4311,C4312 +01/3d/VariableAllocatorAlignmentData_013d28d4.cpp clean error C2821 fable_memory_allocator.h:43 C2821 first formal parameter to 'operator new' must be 'size_t' diff --git a/docs/modern/PERSISTED_TYPES.md b/docs/modern/PERSISTED_TYPES.md new file mode 100644 index 0000000000..3d7e92b021 --- /dev/null +++ b/docs/modern/PERSISTED_TYPES.md @@ -0,0 +1,345 @@ +# Persisted types (step-2 serializer worklist) + +Generated by `tools/modern/persisted_types.py` from Ego_r.pdb names and layouts. Do not edit by +hand; the full table is `PERSISTED_TYPES.tsv`. Feeds [MODERNIZATION](MODERNIZATION.md) step 2 +("Persisted layouts" hazard). + +**Finding.** Saves and defs go member by member through `CPersistContext::Transfer` (and its +relatives) or `CPersistTraits::TransferBinaryIn/Out`, one template instance per leaf type. So +for that path the x64 job is (1) pin the width of every leaf type below to the retail 32-bit +encoding and (2) resolve the pointer leaves (`COpinionDeed *`, `COpinionPersonalityTraits *`), +whose traits must write something other than the address. Class layouts can change freely in the +fork as long as their Transfer methods write the same leaves in the same order. + +**Pointer leaves are resolved (2026-10-16, read from `debug_build/ego_r.exe`): none writes an +address.** `CPersistTraits` serializes the pointee, a 5-element array +of 0x24-byte `COpinionPersonalityTraits` (loop to 0xB4). `CPersistTraits` writes +the deed's fields (SLONG, SLONG, 3D vector, frame, 2 EBOOL, SLONG) and its target object as a +64-bit UID (`WriteUHUGE` of the intelligent pointer's object +0x18). `std::list` +writes a count, then per element a type id (vcall +8) followed by the element's own polymorphic +transfer (vcall +0x10). `std::pair` composes the two. The fork keeps these +encodings as written. The only width rule is that counts, SLONGs and UIDs stay 32/32/64-bit. + +Not covered by this table: file loaders outside CPersistContext (`CMap::LoadFromFile`, +`CLandscapeLayerMesh::Save`, `CThingManager::LoadFromFile`, `CWorld::Load`, bank headers). +Their raw struct images are listed in the last section. Each loader's remaining body still needs +a read when its fork serializer is written. + +2732 persistence symbols read; **195 leaf types** (5 long, 4 pointer, 2 container-count, 1 wchar_t hazards) and **388 classes** with their own persist methods. + +## Leaf types with a width hazard + +| Type | Hazard | Entry points | +| --- | --- | --- | +| `COpinionDeed *` | pointer | CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn,CPersistTraits::TransferOut | +| `COpinionPersonalityTraits *` | pointer | CPersistContext::Transfer,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn | +| `CWideString` | wchar_t | CPersistContext::Transfer,CPersistTraits::TransferIn,CPersistTraits::TransferOut | +| `long` | long | CPersistContext::Transfer,CPersistContext::TransferMap,CPersistContext::TransferMultiMap,CPersistContext::TransferVector,CPersistTraits::TransferOut | +| `std::list >` | container-count | CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn | +| `std::list >` | pointer+container-count | CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn | +| `std::pair` | long | CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn,CPersistTraits::TransferOut | +| `std::pair` | pointer+long | CPersistContext::TransferVector,CPersistTraits::TransferIn | +| `std::pair` | long | CPersistContext::TransferMap,CPersistTraits::TransferOut | +| `unsigned long` | long | CPersistContext::Transfer,CPersistContext::TransferMap,CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn | + +## Leaf types without a width hazard + +`C2DVector`, `C3DAngles`, `C3DVector`, `CAbilityButtonGuiDef`, `CAbilityTrackDef`, `CAbilityTrackLevelDef`, `CActionInputControl`, `CAnimationSet`, `CAppearanceModifierGraphics`, `CAppearanceModifierScalingDef`, `CArenaCreatureDef`, `CArenaRoundDef`, `CArenaWaveDef`, `CAtmosBankEntry`, `CAttackHistoryCombo`, `CBlendedParticleEffectSet`, `CBlurredAttitudesDef`, `CBrainBehaviour`, `CBrainUpdateZone`, `CCardDef`, `CCardPositionsDef`, `CCharString`, `CCombatAbilityData`, `CComboMultiplierData`, `CConversation`, `CCreatureStatsDef`, `CCrimeWaitingForBribe`, `CDefIndex`, `CDefString`, `CDegradableInfo`, `CEngineGraphic`, `CEngineLocalDetailLayerDef`, `CEngineLocalDetailObjectDef`, `CEnvironmentThemeDef`, `CExplosionRing`, `CExpressionSet`, `CFaction`, `CFeatDef`, `CFireEffectCreationSet`, `CFireHeartPatternDef`, `CFloatRange`, `CHeroLogBook::CHeroLogBookEntry::ECategory`, `CHeroMarriageInfo`, `CHeroSoulsCreatureDef`, `CHeroSoulsRoundDef`, `CHeroSoulsWaveDef`, `CHeroStatIncreaseData`, `CIntelligentPointer`, `CLensFlareElementDef`, `CMultiStaticMeshEntryDef`, `CMusicEntry`, `CMusicSetEntry`, `CObjectAugmentationParticleSet`, `CObjectFamilyEntry`, `COpinionDeedReactionDef`, `COpinionTransientOffset`, `CParticleMorphs`, `CPhysicalPrimitiveInit`, `CPrizeScoreDef`, `CQuickAccessItemButtonGuiDef`, `CRGBColour`, `CRandomAppearanceMorph`, `CReactionFrequencyTraitsArray`, `CReverbEnvironmentEntry`, `CShopItemDef`, `CShoveHaPennyRegionDef`, `CSimVoice`, `CSkeletalMorphs`, `CSoundBankEntry`, `CSoundMap`, `CSpecialEffectsStringMap`, `CTCInventoryMap::CUIMiniMapMarker`, `CTCInventoryMap::CUIRegionInfo`, `CTCNoiseDef`, `CTextureMorphs`, `CTextureReplacementDef::CEntry`, `CThingComponentSet`, `CWeaponTrailGraphicSet`, `CWorld::ESaveType`, `CWoundMorphs`, `EActionRegisteredType`, `EClockHandType`, `EClothingSuitPart`, `ECombatCreatureType`, `ECombatSequenceInterruptionType`, `ECombatSequenceIsValidType`, `ECombatSequenceOnStartModuleType`, `ECombatSequenceOnStopModuleType`, `ECombatSequenceType`, `ECombatStrikeRecoilStyle`, `ECompositeBlendType`, `EContextSensitiveType`, `EControlledMovementType`, `EControllerType`, `ECreatureAbility`, `ECreatureGeneratorGenerateType`, `ECreatureInteractionType`, `ECreatureType`, `ECrimeSeverity`, `EDamageAttribute`, `EDoorTriggerType`, `EEngineGraphicType`, `EEngineSprite2DFlag`, `EExpressionInventoryType`, `EFeatAttackType`, `EGameAction`, `EGameEventType`, `EGameflowPosition`, `EGiftType`, `EHeroAbility`, `EHeroAttachableAppearanceModifierType`, `EHeroCentreDoorType`, `EHeroExperienceStatCategory`, `EHeroTitle`, `EHeroTrainingStatus`, `EHitLocationCodelet`, `EInputKey`, `EInventoryActionType`, `EInventoryObjectOrientation`, `ELightingChannel`, `EMessageEventType`, `EMinimapThemeType`, `EMouseButtonControl`, `EMusicSetType`, `ENavigatorType`, `ENoiseType`, `EObjectAugmentationType`, `EOpinionAttitudeType`, `EOpinionDeedType`, `EPSwitchTriggerType`, `EPerceivedThingType`, `EPlaceInHeroSouls`, `EPointLightChannelEffect`, `EQuakeLength`, `EQuakeStrength`, `EReactionSpeechType`, `EReverbEnvironmentType`, `EScriptingStateGroups`, `ESex`, `ESwitchTriggerType`, `ETCInterfaceType`, `ETavernGameControlType`, `EThingCreatureProperty`, `ETrapTriggerType`, `ETrapType`, `ETutorialCategory`, `EUIInventoryCategory`, `EVillageStanceToHero`, `EVillageTaskType`, `EWallMountEffects`, `EWaterType`, `EWeaponClass`, `EWeaponType`, `EWorldMapNameGraphic`, `EXboxControllerButton`, `NGraphicAppearance::CReplaceableMeshes`, `NInventory::CItem`, `NInventory::EInventoryCategoryIdentifier`, `NInventory::EInventoryIdentifier`, `NParticleAttachment::CParticleAttachmentInfo`, `NScript::CQR_EscortTraderScript::EscortTraderQuestStage`, `NScript::CQR_EscortTrader_ManagerScript::EscortTraderManagerPhase`, `NScript::CQ_SunnyvaleMasterData::ArcheryOpenState`, `NScript::CV_AssassinAttacksScript::AA_LocationIdentifier`, `NScript::CV_ExposeMayorScript::PATH_ID`, `NScript::CV_ExposeMayorScript::STAGE_IN_PATH`, `NScript::CV_IntroductionToTrophiesScript::BriarRoseOpinion`, `NScript::CV_MurderTwistFinalScript::EAssassinState`, `NScript::CV_MurderTwistFinalScript::ESheriffState`, `NSpeechGainManager::CDialogueLayerDef`, `NSpeechGainManager::EDialogueLayer`, `NUISystem::CUIStateDef`, `NUISystem::EActionType`, `NUISystem::EStateChangeType`, `NUISystem::ETableExpansionTypes`, `NUISystem::ETextAlignement`, `NUISystem::EType`, `bool`, `double`, `float`, `int`, `short`, `signed char`, `unsigned __int64`, `unsigned char` + +## Persisting classes with pointer, long or wchar_t members + +Pointer members are fine unless a Transfer method writes them; each needs one look at its +Transfer body when the fork's serializer for that class is written. + +| Class | PDB size | Persist methods | Pointers | long | wchar_t | +| --- | ---: | --- | ---: | ---: | ---: | +| `CManager` | 256 | Load,Save | 17 | 5 | 1 | +| `CLandscapeLayerMesh` | 84 | Save | 9 | 7 | 0 | +| `CLandscapeBackgroundPatch` | 284 | Load,Save | 9 | 1 | 0 | +| `CThingManager` | 272 | LoadFromFile | 8 | 3 | 1 | +| `CPersistContext` | 44 | PersistError,TransferObjectLoadText,TransferObjectSaveText,TransferSubComponentLoadText,TransferSubComponentSaveText,TransferVectorLoadText,TransferVectorOfSubComponentsLoadText,TransferVectorSaveText | 8 | 0 | 0 | +| `CThing` | 168 | Save | 7 | 2 | 0 | +| `CGuiWindowManager` | 136 | Load,Save | 6 | 1 | 0 | +| `CWorld` | 440 | Load | 5 | 8 | 2 | +| `CEngineWaterBackgroundSubPatch` | 96 | Load,Save | 5 | 3 | 0 | +| `CLocalDetailPrimitiveRepeatedMesh` | 96 | Load,Save | 5 | 2 | 0 | +| `CObjectTypeCollection` | 28 | Load,Save | 5 | 0 | 0 | +| `CQuestManager` | 276 | TransferBoasts | 4 | 5 | 0 | +| `CEngineLandscapePatch` | 72 | Save | 4 | 2 | 0 | +| `CNavQuadTreeNode` | 32 | SaveToFile | 4 | 0 | 0 | +| `CMap` | 7504 | LoadFromFile,SaveToFile | 3 | 9 | 0 | +| `CLocalDetailObjectCollectionType` | 84 | Load,Save | 3 | 4 | 0 | +| `CWaterPatchMesh` | 32 | Load,Save | 3 | 0 | 0 | +| `CThingCreatureBase` | 392 | Save | 2 | 5 | 0 | +| `CWorldMap` | 252 | LoadFromFile,SaveToFile | 2 | 5 | 0 | +| `CGameTimeManager` | 64 | Load,Save | 2 | 2 | 0 | +| `CGuiVarTransferDefIndex` | 28 | TransferFromGui,TransferToGui | 2 | 1 | 0 | +| `CGuiVarTransferSymbolVal` | 28 | TransferFromGui,TransferToGui | 2 | 1 | 0 | +| `CLocalDetailPrimitiveMeshZSpriteBatch` | 80 | Load,Save | 2 | 1 | 0 | +| `CObject` | 40 | Load | 2 | 1 | 0 | +| `CVolumeTexture` | 16 | LoadFromFile,SaveToFile | 2 | 1 | 0 | +| `COpinionReactionManagerDef` | 860 | Transfer | 2 | 0 | 0 | +| `CUserProfileManager` | 324 | Load,Save | 1 | 10 | 5 | +| `CEngineProfileLog` | 1592 | Save | 1 | 4 | 1 | +| `CGuiVarTransferSLONG` | 32 | TransferFromGui,TransferToGui | 1 | 4 | 0 | +| `CGuiVarTransferULONG` | 32 | TransferFromGui,TransferToGui | 1 | 4 | 0 | +| `CReactionFrequencyTraits_ControlledCount` | 44 | TransferBinaryIn,TransferBinaryOut | 1 | 3 | 0 | +| `CEditWorldMap` | 332 | LoadFromFile,SaveToFile | 1 | 2 | 0 | +| `CThingAICreature` | 528 | Save | 1 | 2 | 0 | +| `CEngineTexturePalette` | 28 | Load,Save | 1 | 1 | 0 | +| `CBase` | 200 | TransferPersistantBaseData,TransferSubComponentsFrom | 1 | 0 | 0 | +| `CCharString` | 4 | LoadFromFile,SaveToFile | 1 | 0 | 0 | +| `CGuiVarTransferBool` | 20 | TransferFromGui,TransferToGui | 1 | 0 | 0 | +| `CGuiVarTransferDefIndexList` | 36 | TransferFromGui,TransferToGui | 1 | 0 | 0 | +| `CGuiVarTransferFloat` | 32 | TransferFromGui,TransferToGui | 1 | 0 | 0 | +| `CGuiVarTransferString` | 20 | TransferFromGui,TransferToGui | 1 | 0 | 0 | +| `CGuiVarTransferStringList` | 36 | TransferFromGui,TransferToGui | 1 | 0 | 0 | +| `CNavNavigableLeafNode` | 32 | SaveToFile | 1 | 0 | 0 | +| `COpinionPersonalityDef` | 92 | Transfer | 1 | 0 | 0 | +| `CTCBase` | 12 | Serialise | 1 | 0 | 0 | +| `CTexture` | 8 | LoadFromFile,SaveToFile | 1 | 0 | 0 | +| `CWideString` | 4 | LoadFromFile,SaveToFile | 1 | 0 | 0 | +| `CUIMiscThingsDef` | 620 | Transfer | 0 | 10 | 70 | +| `CUIDef` | 604 | Transfer | 0 | 18 | 2 | +| `CChecksumCacheFile` | 28 | Save | 0 | 1 | 2 | +| `CScriptDef` | 4436 | Transfer | 0 | 548 | 0 | +| `CPlayerGuiDef` | 2896 | Transfer | 0 | 219 | 0 | +| `CInventoryDef` | 2656 | Transfer | 0 | 142 | 0 | +| `CTavernGameDef` | 424 | Transfer | 0 | 62 | 0 | +| `CEnvironmentDef` | 312 | Transfer | 0 | 45 | 0 | +| `CGlobalDef` | 352 | Transfer | 0 | 38 | 0 | +| `CUIIconsDef` | 192 | Transfer | 0 | 32 | 0 | +| `CVillagePeopleDef` | 204 | Transfer | 0 | 29 | 0 | +| `CVillageDef` | 300 | Transfer | 0 | 26 | 0 | +| `CCreatureDef` | 252 | Transfer | 0 | 19 | 0 | +| `CHeroStatsDef` | 356 | Transfer | 0 | 19 | 0 | +| `CCombatDialogueDef` | 144 | Transfer | 0 | 17 | 0 | +| `CEngineThemeDef` | 168 | Transfer | 0 | 16 | 0 | +| `CThingCreatureDef` | 340 | Transfer | 0 | 15 | 0 | +| `CRegionDef` | 208 | Transfer | 0 | 11 | 0 | +| `CGuardDef` | 80 | Transfer | 0 | 10 | 0 | +| `COracleMinigameDef` | 92 | Transfer | 0 | 10 | 0 | +| `CShopDef` | 224 | Transfer | 0 | 10 | 0 | +| `CSkyDef` | 136 | Transfer | 0 | 10 | 0 | +| `CHeroMarriageDef` | 200 | Transfer | 0 | 9 | 0 | +| `CObjectAugmentationsDef` | 160 | Transfer | 0 | 9 | 0 | +| `CQuestCardDef` | 124 | Transfer | 0 | 9 | 0 | +| `CThingShotDef` | 176 | Transfer | 0 | 9 | 0 | +| `CAIScratchpadDef` | 156 | Transfer | 0 | 8 | 0 | +| `CBettingDef` | 88 | Transfer | 0 | 8 | 0 | +| `CEngineDef` | 132 | Transfer | 0 | 8 | 0 | +| `CHeroAbilityDef` | 172 | Transfer | 0 | 8 | 0 | +| `CHeroTitleDef` | 80 | Transfer | 0 | 8 | 0 | +| `CWeaponDef` | 244 | Transfer | 0 | 8 | 0 | +| `CBonusItemDef` | 116 | Transfer | 0 | 7 | 0 | +| `CInventoryItemDef` | 112 | Transfer | 0 | 7 | 0 | +| `CSummonDef` | 76 | Transfer | 0 | 7 | 0 | +| `CConfigOptionsDefaultsDef` | 120 | Transfer | 0 | 6 | 0 | +| `CEnvironmentThemeDef` | 200 | Transfer | 0 | 6 | 0 | +| `CKrakenDef` | 128 | Transfer | 0 | 6 | 0 | +| `CWound` | 68 | TransferBinaryIn,TransferBinaryOut | 0 | 6 | 0 | +| `CCreatureStatsDef` | 72 | Transfer | 0 | 5 | 0 | +| `CExplosionDef` | 116 | Transfer | 0 | 5 | 0 | +| `CHitLocationDef` | 128 | Transfer | 0 | 5 | 0 | +| `CMeleeCombatKnockdownEffects` | 96 | Transfer | 0 | 5 | 0 | +| `COpinionDeedEffectsDef` | 148 | Transfer | 0 | 5 | 0 | +| `CReactionFrequencyTraits_PeriodicFrequency` | 32 | TransferBinaryIn,TransferBinaryOut | 0 | 5 | 0 | +| `CTattooDef` | 96 | Transfer | 0 | 5 | 0 | +| `CThingBuildingDef` | 168 | Transfer | 0 | 5 | 0 | +| `CThingObjectDef` | 160 | Transfer | 0 | 5 | 0 | +| `CTrapDef` | 104 | Transfer | 0 | 5 | 0 | +| `CAugmentationDef` | 76 | Transfer | 0 | 4 | 0 | +| `CCameraManagerSetDef` | 80 | Transfer | 0 | 4 | 0 | +| `CCarryableDef` | 80 | Transfer | 0 | 4 | 0 | +| `CContextSensitiveItemDef` | 60 | Transfer | 0 | 4 | 0 | +| `CEngineLocalDetailObjectDef` | 140 | Transfer | 0 | 4 | 0 | +| `CFeatDef` | 100 | Transfer | 0 | 4 | 0 | +| `CJackDragonDef` | 120 | Transfer | 0 | 4 | 0 | +| `CMeleeCombatAbilityDef` | 136 | Transfer | 0 | 4 | 0 | +| `CRegionScriptDef` | 80 | Transfer | 0 | 4 | 0 | +| `CAnimationSet` | 36 | TransferBinaryIn,TransferBinaryOut | 0 | 3 | 0 | +| `CAppearanceModifierDef` | 120 | Transfer | 0 | 3 | 0 | +| `CBriarRoseDef` | 84 | Transfer | 0 | 3 | 0 | +| `CCameraManagerDef` | 128 | Transfer | 0 | 3 | 0 | +| `CCameraModeDef` | 220 | Transfer | 0 | 3 | 0 | +| `CChestDef` | 60 | Transfer | 0 | 3 | 0 | +| `CCreatureModeDef` | 68 | Transfer | 0 | 3 | 0 | +| `CFireheartMinigameDef` | 60 | Transfer | 0 | 3 | 0 | +| `CFishingDef` | 128 | Transfer | 0 | 3 | 0 | +| `CFrontEndDef` | 92 | Transfer | 0 | 3 | 0 | +| `CHasNameDef` | 52 | Transfer | 0 | 3 | 0 | +| `CInventoryCategoryDef` | 92 | Transfer | 0 | 3 | 0 | +| `CMessageEventDef` | 84 | Transfer | 0 | 3 | 0 | +| `CNymphDef` | 80 | Transfer | 0 | 3 | 0 | +| `COpinionOfHeroDef` | 60 | Transfer | 0 | 3 | 0 | +| `COpinionTransientOffset` | 24 | TransferBinaryIn | 0 | 3 | 0 | +| `CPlayerMovementDef` | 128 | Transfer | 0 | 3 | 0 | +| `CShipDef` | 68 | Transfer | 0 | 3 | 0 | +| `CSimBuildingDef` | 76 | Transfer | 0 | 3 | 0 | +| `CSnowTrollDef` | 96 | Transfer | 0 | 3 | 0 | +| `CSpecialAbilitiesBerserkDef` | 468 | Transfer | 0 | 3 | 0 | +| `CStockItemDef` | 64 | Transfer | 0 | 3 | 0 | +| `CSummonerDef` | 92 | Transfer | 0 | 3 | 0 | +| `CTavernGameShoveHaPennyDef` | 520 | Transfer | 0 | 3 | 0 | +| `CTavernGameSpotTheAdditionDef` | 152 | Transfer | 0 | 3 | 0 | +| `CThingMultiArrowShotDef` | 84 | Transfer | 0 | 3 | 0 | +| `CThunderBattleDef` | 80 | Transfer | 0 | 3 | 0 | +| `CTrollBattleDef` | 100 | Transfer | 0 | 3 | 0 | +| `CTurncoatDef` | 88 | Transfer | 0 | 3 | 0 | +| `CWaterSeaStaticMapInfoBlock` | 12 | Load,Save | 0 | 3 | 0 | +| `CAbilityTrackLevelDef` | 76 | Transfer | 0 | 2 | 0 | +| `CAnimComponentCombatComboChain` | 16 | TransferBinaryIn,TransferBinaryOut | 0 | 2 | 0 | +| `CAreaOfEffectAttackDef` | 80 | Transfer | 0 | 2 | 0 | +| `CArenaCreatureDef` | 56 | Transfer | 0 | 2 | 0 | +| `CArmourDef` | 124 | Transfer | 0 | 2 | 0 | +| `CBlendedParticleEffectSet` | 16 | TransferBinaryIn,TransferBinaryOut | 0 | 2 | 0 | +| `CBuyableHouseDef` | 84 | Transfer | 0 | 2 | 0 | +| `CCardDef` | 72 | Transfer | 0 | 2 | 0 | +| `CCrateStackDef` | 48 | Transfer | 0 | 2 | 0 | +| `CCreatureGenerationFamilyDef` | 104 | Transfer | 0 | 2 | 0 | +| `CCreatureGeneratorDef` | 68 | Transfer | 0 | 2 | 0 | +| `CDecapitationDef` | 48 | Transfer | 0 | 2 | 0 | +| `CDoorDef` | 60 | Transfer | 0 | 2 | 0 | +| `CEngineStaticMapEdgeHeights` | 232 | Load,Save | 0 | 2 | 0 | +| `CEngineVideoOptionsDef` | 124 | Transfer | 0 | 2 | 0 | +| `CExplodingObjectDef` | 52 | Transfer | 0 | 2 | 0 | +| `CExpressionDef` | 148 | Transfer | 0 | 2 | 0 | +| `CFishDef` | 88 | Transfer | 0 | 2 | 0 | +| `CGraphicHeader` | 28 | Load,Save | 0 | 2 | 0 | +| `CGuiVarTransferExclusiveOptions` | 52 | TransferFromGui,TransferToGui | 0 | 2 | 0 | +| `CHeroCombatDef` | 372 | Transfer | 0 | 2 | 0 | +| `CHighlightItemDef` | 72 | Transfer | 0 | 2 | 0 | +| `CKickableDef` | 84 | Transfer | 0 | 2 | 0 | +| `CLightningDef` | 92 | Transfer | 0 | 2 | 0 | +| `CLightningOrbDef` | 60 | Transfer | 0 | 2 | 0 | +| `CMaterialDef` | 180 | Transfer | 0 | 2 | 0 | +| `CMazeBattleDef` | 100 | Transfer | 0 | 2 | 0 | +| `CSoundThemeDef` | 108 | Transfer | 0 | 2 | 0 | +| `CSpecialAbilitiesAssassinRushDef` | 148 | Transfer | 0 | 2 | 0 | +| `CSpecialAbilitiesDrunkennessDef` | 84 | Transfer | 0 | 2 | 0 | +| `CSpecialAbilitiesForcePushDef` | 180 | Transfer | 0 | 2 | 0 | +| `CSpecialAbilitiesMultiArrowDef` | 140 | Transfer | 0 | 2 | 0 | +| `CTavernGameCardBaseDef` | 140 | Transfer | 0 | 2 | 0 | +| `CTavernGameCoinBaseDef` | 68 | Transfer | 0 | 2 | 0 | +| `CTavernGameCoinGolfDef` | 92 | Transfer | 0 | 2 | 0 | +| `CThingBaseDef` | 116 | Transfer | 0 | 2 | 0 | +| `CTrophyDef` | 100 | Transfer | 0 | 2 | 0 | +| `CUIStateDef` | 132 | Transfer | 0 | 2 | 0 | +| `CWaterStaticMapInfoBlock` | 8 | Load,Save | 0 | 2 | 0 | +| `CWhisperBattleDef` | 72 | Transfer | 0 | 2 | 0 | +| `CAbilityButtonGuiDef` | 96 | Transfer | 0 | 1 | 0 | +| `CAbilityTrackDef` | 88 | Transfer | 0 | 1 | 0 | +| `CAnimComponentCombatRecoil` | 12 | TransferBinaryIn,TransferBinaryOut | 0 | 1 | 0 | +| `CAnimComponentDelayTime` | 12 | TransferBinaryIn,TransferBinaryOut | 0 | 1 | 0 | +| `CAnimComponentFlags` | 12 | TransferBinaryIn,TransferBinaryOut | 0 | 1 | 0 | +| `CAnimComponentTransitionInTime` | 12 | TransferBinaryIn,TransferBinaryOut | 0 | 1 | 0 | +| `CAnimationEntry` | 16 | TransferBinaryIn,TransferBinaryOut | 0 | 1 | 0 | +| `CArenaRoundDef` | 60 | Transfer | 0 | 1 | 0 | +| `CArenaWaveDef` | 64 | Transfer | 0 | 1 | 0 | +| `CBossDef` | 96 | Transfer | 0 | 1 | 0 | +| `CCameraCollisionDef` | 44 | Transfer | 0 | 1 | 0 | +| `CCarrySlotDef` | 56 | Transfer | 0 | 1 | 0 | +| `CCombatAbilityData` | 24 | TransferBinaryIn,TransferBinaryOut | 0 | 1 | 0 | +| `CCombatSequenceDef` | 112 | Transfer | 0 | 1 | 0 | +| `CCombatTypeDef` | 192 | Transfer | 0 | 1 | 0 | +| `CConversation` | 108 | Transfer | 0 | 1 | 0 | +| `CDefString` | 4 | TransferBinaryIn | 0 | 1 | 0 | +| `CDragonActionSwoopDef` | 100 | Transfer | 0 | 1 | 0 | +| `CEnemyDef` | 44 | Transfer | 0 | 1 | 0 | +| `CEngineMapDependencyChecker` | 20 | Load,Save | 0 | 1 | 0 | +| `CEntitySoundDef` | 92 | Transfer | 0 | 1 | 0 | +| `CEnvironmentThemeDaySetDef` | 116 | Transfer | 0 | 1 | 0 | +| `CExperienceDef` | 92 | Transfer | 0 | 1 | 0 | +| `CExplosionRing` | 16 | TransferBinaryIn,TransferBinaryOut | 0 | 1 | 0 | +| `CExplosiveTrailDef` | 76 | Transfer | 0 | 1 | 0 | +| `CExpressionSubDef` | 44 | Transfer | 0 | 1 | 0 | +| `CFireballSpellLevelDef` | 44 | Transfer | 0 | 1 | 0 | +| `CFlammableDef` | 80 | Transfer | 0 | 1 | 0 | +| `CGoldDef` | 44 | Transfer | 0 | 1 | 0 | +| `CHairCardDef` | 44 | Transfer | 0 | 1 | 0 | +| `CHeroDef` | 48 | Transfer | 0 | 1 | 0 | +| `CHeroSoulsCreatureDef` | 52 | Transfer | 0 | 1 | 0 | +| `CHeroSoulsRoundDef` | 60 | Transfer | 0 | 1 | 0 | +| `CHeroSoulsWaveDef` | 60 | Transfer | 0 | 1 | 0 | +| `CInputEventPackage` | 52 | Save | 0 | 1 | 0 | +| `CJackOfBladesBattleDef` | 132 | Transfer | 0 | 1 | 0 | +| `CLensFlareElementDef` | 80 | Transfer | 0 | 1 | 0 | +| `CLightDef` | 92 | Transfer | 0 | 1 | 0 | +| `CMultiStaticMeshEntryDef` | 56 | Transfer | 0 | 1 | 0 | +| `CNavQuadTree` | 176 | LoadFromFile,SaveToFile | 0 | 1 | 0 | +| `COccupiableDef` | 44 | Transfer | 0 | 1 | 0 | +| `COpinionDeedReactionDef` | 88 | Transfer | 0 | 1 | 0 | +| `COpinionSourceDef` | 216 | Transfer | 0 | 1 | 0 | +| `CParticleEmitterDatabase` | 88 | Load,Save,Serialise | 0 | 1 | 0 | +| `CPhysicsDef` | 68 | Transfer | 0 | 1 | 0 | +| `CPlayerDef` | 72 | Transfer | 0 | 1 | 0 | +| `CPlayerInventoryDef` | 136 | Transfer | 0 | 1 | 0 | +| `CPrizeScoreDef` | 48 | Transfer | 0 | 1 | 0 | +| `CQuickAccessItemButtonGuiDef` | 84 | Transfer | 0 | 1 | 0 | +| `CRandomAppearanceMorph` | 76 | TransferBinaryIn,TransferBinaryOut | 0 | 1 | 0 | +| `CReactionFrequencyTraits_LinearTime` | 16 | TransferBinaryIn,TransferBinaryOut | 0 | 1 | 0 | +| `CResurrectionItemDef` | 44 | Transfer | 0 | 1 | 0 | +| `CScorpionKingBattleDef` | 100 | Transfer | 0 | 1 | 0 | +| `CShopItemDef` | 72 | Transfer | 0 | 1 | 0 | +| `CSmashableDef` | 48 | Transfer | 0 | 1 | 0 | +| `CSmokeGeneratorDef` | 48 | Transfer | 0 | 1 | 0 | +| `CSpecialAbilitiesBulletTimeDef` | 428 | Transfer | 0 | 1 | 0 | +| `CSpecialAbilitiesSummonSpellDef` | 120 | Transfer | 0 | 1 | 0 | +| `CSpecialAbilitiesThunderLightningStormDef` | 108 | Transfer | 0 | 1 | 0 | +| `CTGAFile` | 12 | Load | 0 | 1 | 0 | +| `CTargetingDef` | 148 | Transfer | 0 | 1 | 0 | +| `CTavernDef` | 44 | Transfer | 0 | 1 | 0 | +| `CTeleporterDef` | 52 | Transfer | 0 | 1 | 0 | +| `CThingMarkerDef` | 140 | Transfer | 0 | 1 | 0 | +| `CThingPhysical` | 216 | Save | 0 | 1 | 0 | +| `CThingPhysicalSwitchDef` | 152 | Transfer | 0 | 1 | 0 | +| `CThingSwitch` | 256 | Save | 0 | 1 | 0 | +| `CUILocaleGraphicsDef` | 100 | Transfer | 0 | 1 | 0 | +| `CWallMountEffectsDef` | 52 | Transfer | 0 | 1 | 0 | +| `CWaspQueenBattleDef` | 68 | Transfer | 0 | 1 | 0 | +| `CWifeDef` | 44 | Transfer | 0 | 1 | 0 | + +## Persisting classes with none of those members (143) + +`CAICreatureWillPowerIndicatorDef`, `CANavQuadTreeNode`, `CAbilityDef`, `CActionInputControl`, `CActionUseDef`, `CActivateQuestDef`, `CAnimComponentCombatFlourishTargetOffset`, `CAnimComponentCombatHandedness`, `CAnimComponentCombatMisc`, `CAnimComponentFollowingOnAnim`, `CAnimComponentFollowingOnAnimFilter`, `CAnimComponentSetCreatureMode`, `CAnimComponentStrikeResponseAnim`, `CAnimComponentStrikeSpeedMultiplier`, `CAnimatingObjectDef`, `CAppearanceDef`, `CAppearanceModifierGraphics`, `CAppearanceModifierScalingDef`, `CAttackPatternDef`, `CBalverineBattleDef`, `CBedDef`, `CBlurredAttitudesDef`, `CBoastingPodiumDef`, `CBrainDef`, `CBuyHouseDef`, `CCRCSymbolMap`, `CCardPositionsDef`, `CCarriedReadableDef`, `CCarryingDef`, `CClockDef`, `CCoinGameObstacleDef`, `CCombatAbilityAttackBase`, `CCombatAbilityBlockDefBase`, `CCombatAbilityStrafeDef`, `CCombatAbilityUseProjectileWeaponDef`, `CContainerRewardHeroDef`, `CControlsDef`, `CCoopSpiritDef`, `CCreatureAbilityDef`, `CCreatureNavigationDef`, `CCutsceneDef`, `CDegradableDef`, `CDialogueLayerDef`, `CDragonActionHoverDef`, `CDragonActionNapalmDef`, `CDrunkennessDef`, `CEditMapBrush`, `CEngineLocalDetailGeneratorDef`, `CEngineLocalDetailLayerDef`, `CEngineThemeGroupDef`, `CExpressionSet`, `CFactionDef`, `CFireHeartPatternDef`, `CFishingRodDef`, `CGiftDef`, `CGuildMasterDef`, `CHeroCentreDef`, `CHeroExperienceDef`, `CHeroMorphDef`, `CHeroPostcardGeneratorDef`, `CHeroSpecialMovementDef`, `CHeroSuitDef`, `CHitLocationsDef`, `CIdleSchedulerDef`, `CInterestingToVillagersDef`, `CKrakenTentacleDef`, `CLandscapeBackgroundSubNodeSplitBranch`, `CLocalDetailPrimitiveMesh`, `CLookDef`, `CMultiStaticMeshDef`, `CNavBlockedQuadTreeNode`, `CNavSwitchableLeafNode`, `CObjectFamilyDef`, `CObjectTypeCollectionPalette`, `COpinionDeedMaskDef`, `COpinionPersonalityTraits`, `COpinionReactionMaskDef`, `COpinionSet`, `COverheadDisplayDef`, `CParticleAttacherDef`, `CParticleMorphs`, `CPatchTesselationEdgeStrip`, `CPerceivedThingDef`, `CPersistTraitsSLONG`, `CPhysicalPrimitiveCylinder`, `CPhysicalPrimitiveMesh`, `CPhysicalPrimitiveSphere`, `CReactionMatch`, `CReactionMatch_AsymmetricPyramid`, `CReactionMatch_MRPyramid`, `CReactionMatch_SecondaryAxisCentredMRShifted`, `CReactionMatch_SecondaryAxisMRShifted`, `CReactionMatch_Sphere`, `CReadableDef`, `CRegion`, `CReplaceableMeshDef`, `CRumbleDef`, `CShoveHaPennyRegionDef`, `CSimVoice`, `CSimVoicesDef`, `CSkeletalMorphDef`, `CSkeletalMorphs`, `CSoundAtmospheresDef`, `CSoundDef`, `CSoundMap`, `CSpecialAbilitiesBattleChargeDef`, `CSpecialAbilitiesBurntEffectDef`, `CSpecialAbilitiesCreatureTintDef`, `CSpecialAbilitiesDrainLifeDef`, `CSpecialAbilitiesElectrocutedEffectDef`, `CSpecialAbilitiesEnflameDef`, `CSpecialAbilitiesFireballSpellDef`, `CSpecialAbilitiesForcePushDataDef`, `CSpecialAbilitiesGhostSwordDef`, `CSpecialAbilitiesHealLifeDef`, `CSpecialAbilitiesLightningSpellDef`, `CSpecialAbilitiesMultiStrikeDef`, `CSpecialAbilitiesPhysicalShieldDef`, `CSpecialAbilitiesTurncoatSpellDef`, `CSpecialAbilitiesUnholyPowerDef`, `CSpecialEffectsDef`, `CSpotLightDef`, `CStealthDef`, `CSummonableCreatureDef`, `CSymbolMap`, `CTCNoiseDef`, `CTCVolumeContainmentTrackerDef`, `CTavernTableDef`, `CTextureMorphs`, `CTextureReplacementDef`, `CThingComponentSet`, `CThingDrainLifeShotDef`, `CThingHolySiteDef`, `CThingNoiseDef`, `CThingPhysicalSwitch`, `CThingSwitchDef`, `CThingTrackNode`, `CThingVillageDef`, `CTimeAppearanceFadeDef`, `CVillageMemberDef`, `CVillagerInteractionsDef`, `CWillResponseDef`, `CWoundMorphs` + +## Raw struct images (map/level files) + +Read and written whole by `CAFile::ReadVar` / `WriteVar`, or by +`CReplaceSerialise`, which reads an old file-format version and upgrades it. Odd sizes +(25, 73 bytes) show these are packed. The fork must keep them byte-identical: `#pragma pack(1)`, +`int32_t` for every `long`, and no pointers (none has a pointer or STL member). Members are +counted through nested structs. PDB sizes are Ego_r's, so check them against retail reads +before relying on them. + +| Type | PDB size | Via | long | pointer | STL | +| --- | ---: | --- | ---: | ---: | ---: | +| `C3DVector` | 12 | CAFile::ReadVar | 0 | 0 | 0 | +| `CFileFormatBrush` | 73 | CReplaceSerialise | 8 | 0 | 0 | +| `CFileFormatEditMapBrushHeader` | 33836 | CReplaceSerialise | 7 | 0 | 0 | +| `CFileFormatHeightMapCell` | 21 | CReplaceSerialise | 0 | 0 | 0 | +| `CFileFormatLevel` | 25 | CReplaceSerialise | 5 | 0 | 0 | +| `CFileFormatMapHeader` | 67618 | CReplaceSerialise | 7 | 0 | 0 | +| `CFileFormatThingBrushInfo` | 92 | CReplaceSerialise | 2 | 0 | 0 | +| `COldFileFormatBrush` | 61 | CReplaceSerialise | 5 | 0 | 0 | +| `COldFileFormatEditMapBrushHeader` | 33832 | CReplaceSerialise | 6 | 0 | 0 | +| `COldFileFormatHeightMapCell1` | 9 | CReplaceSerialise | 0 | 0 | 0 | +| `COldFileFormatHeightMapCell2` | 10 | CReplaceSerialise | 0 | 0 | 0 | +| `COldFileFormatHeightMapCell3` | 15 | CReplaceSerialise | 0 | 0 | 0 | +| `COldFileFormatHeightMapCell4` | 17 | CReplaceSerialise | 0 | 0 | 0 | +| `COldFileFormatHeightMapCell5` | 25 | CReplaceSerialise | 2 | 0 | 0 | +| `COldFileFormatHeightMapCell6` | 21 | CReplaceSerialise | 0 | 0 | 0 | +| `COldFileFormatLevel1` | 13 | CReplaceSerialise | 2 | 0 | 0 | +| `COldFileFormatLevel2` | 17 | CReplaceSerialise | 3 | 0 | 0 | +| `COldFileFormatLevel3` | 21 | CReplaceSerialise | 4 | 0 | 0 | +| `COldFileFormatMapHeader1` | | CReplaceSerialise | 0 | 0 | 0 | +| `COldFileFormatMapHeader2` | | CReplaceSerialise | 0 | 0 | 0 | +| `COldFileFormatMapHeader3` | | CReplaceSerialise | 0 | 0 | 0 | +| `COldFileFormatMapHeader4` | 33806 | CReplaceSerialise | 3 | 0 | 0 | +| `COldFileFormatMapHeader5` | 33814 | CReplaceSerialise | 3 | 0 | 0 | +| `COldFileFormatMapHeader6` | 33818 | CReplaceSerialise | 4 | 0 | 0 | +| `COldFileFormatMapHeader7` | 33822 | CReplaceSerialise | 5 | 0 | 0 | +| `COldFileFormatMapHeader8` | 67614 | CReplaceSerialise | 6 | 0 | 0 | +| `COldFileFormatThingBrushInfo` | 88 | CReplaceSerialise | 2 | 0 | 0 | +| `int` | | CAFile::ReadVar | 0 | 0 | 0 | +| `unsigned char` | | CAFile::ReadVar | 0 | 0 | 0 | diff --git a/docs/modern/PERSISTED_TYPES.tsv b/docs/modern/PERSISTED_TYPES.tsv new file mode 100644 index 0000000000..888c41a19e --- /dev/null +++ b/docs/modern/PERSISTED_TYPES.tsv @@ -0,0 +1,584 @@ +kind type persist_entry hazard pdb_size pointer_members long_members wchar_members +leaf C2DVector CPersistContext::Transfer,CPersistContext::TransferVector,CPersistTraits::TransferIn,CPersistTraits::TransferOut 8 +leaf C3DAngles CPersistTraits::TransferIn,CPersistTraits::TransferOut 12 +leaf C3DVector CPersistContext::Transfer,CPersistTraits::TransferIn,CPersistTraits::TransferOut 12 +leaf CAbilityButtonGuiDef CPersistContext::TransferVectorOfSubComponents 96 +leaf CAbilityTrackDef CPersistContext::TransferVectorOfSubComponents 88 +leaf CAbilityTrackLevelDef CPersistContext::TransferVectorOfSubComponents 76 +leaf CActionInputControl CPersistContext::TransferVector,CPersistTraits::TransferIn,CPersistTraits::TransferOut 28 +leaf CAnimationSet CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferIn 36 +leaf CAppearanceModifierGraphics CPersistContext::Transfer,CPersistTraits::TransferIn 16 +leaf CAppearanceModifierScalingDef CPersistContext::TransferVectorOfSubComponents 76 +leaf CArenaCreatureDef CPersistContext::TransferVectorOfSubComponents 56 +leaf CArenaRoundDef CPersistContext::TransferVectorOfSubComponents 60 +leaf CArenaWaveDef CPersistContext::TransferVectorOfSubComponents 64 +leaf CAtmosBankEntry CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn 8 +leaf CAttackHistoryCombo CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn 20 +leaf CBlendedParticleEffectSet CPersistContext::TransferMap,CPersistTraits::TransferIn 16 +leaf CBlurredAttitudesDef CPersistContext::TransferVectorOfSubComponents 80 +leaf CBrainBehaviour CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn 12 +leaf CBrainUpdateZone CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn 12 +leaf CCardDef CPersistContext::TransferVectorOfSubComponents 72 +leaf CCardPositionsDef CPersistContext::TransferVectorOfSubComponents 80 +leaf CCharString CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistContext::TransferMap,CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn,CPersistTraits::TransferOut 4 +leaf CCombatAbilityData CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferIn 24 +leaf CComboMultiplierData CPersistContext::GetDefaultVal,CPersistContext::TransferMap,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn 24 +leaf CConversation CPersistContext::TransferVectorOfSubComponents 108 +leaf CCreatureStatsDef CPersistContext::TransferSubComponent 72 +leaf CCrimeWaitingForBribe CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn,CPersistTraits::TransferOut 8 +leaf CDefIndex CPersistContext::Transfer,CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn,CPersistTraits::TransferOut 4 +leaf CDefString CPersistContext::Transfer,CPersistContext::TransferMap,CPersistContext::TransferVector,CPersistTraits::TransferIn,CPersistTraits::TransferOut 4 +leaf CDegradableInfo CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn 28 +leaf CEngineGraphic CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn,CPersistTraits::TransferOut 14 +leaf CEngineLocalDetailLayerDef CPersistContext::TransferVectorOfSubComponents 96 +leaf CEngineLocalDetailObjectDef CPersistContext::TransferVectorOfSubComponents 140 +leaf CEnvironmentThemeDef CPersistContext::TransferVectorOfSubComponents 200 +leaf CExplosionRing CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn 16 +leaf CExpressionSet CPersistContext::Transfer,CPersistTraits::TransferIn 20 +leaf CFaction CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn,CPersistTraits::TransferOut 36 +leaf CFeatDef CPersistContext::TransferVectorOfSubComponents 100 +leaf CFireEffectCreationSet CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferIn 24 +leaf CFireHeartPatternDef CPersistContext::TransferVectorOfSubComponents 60 +leaf CFloatRange CPersistContext::Transfer,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn 8 +leaf CHeroLogBook::CHeroLogBookEntry::ECategory CPersistContext::TransferVector +leaf CHeroMarriageInfo CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn,CPersistTraits::TransferOut 24 +leaf CHeroSoulsCreatureDef CPersistContext::TransferVectorOfSubComponents 52 +leaf CHeroSoulsRoundDef CPersistContext::TransferVectorOfSubComponents 60 +leaf CHeroSoulsWaveDef CPersistContext::TransferVectorOfSubComponents 60 +leaf CHeroStatIncreaseData CPersistContext::GetDefaultVal,CPersistContext::TransferMap,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn 16 +leaf CIntelligentPointer CPersistContext::Transfer,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn +leaf CLensFlareElementDef CPersistContext::TransferVectorOfSubComponents 80 +leaf CMultiStaticMeshEntryDef CPersistContext::TransferVectorOfSubComponents 56 +leaf CMusicEntry CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn 16 +leaf CMusicSetEntry CPersistContext::TransferVector,CPersistTraits::TransferIn 12 +leaf CObjectAugmentationParticleSet CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistContext::TransferMap,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn 52 +leaf CObjectFamilyEntry CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn 8 +leaf COpinionDeed * CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn,CPersistTraits::TransferOut pointer +leaf COpinionDeedReactionDef CPersistContext::TransferVectorOfSubComponents 88 +leaf COpinionPersonalityTraits * CPersistContext::Transfer,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn pointer +leaf COpinionTransientOffset CPersistContext::Transfer,CPersistTraits::TransferIn,CPersistTraits::TransferOut 24 +leaf CParticleMorphs CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferIn 20 +leaf CPhysicalPrimitiveInit CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn 8 +leaf CPrizeScoreDef CPersistContext::TransferVectorOfSubComponents 48 +leaf CQuickAccessItemButtonGuiDef CPersistContext::TransferVectorOfSubComponents 84 +leaf CRGBColour CPersistContext::Transfer,CPersistContext::TransferVector,CPersistTraits::TransferIn,CPersistTraits::TransferOut 4 +leaf CRandomAppearanceMorph CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferIn 76 +leaf CReactionFrequencyTraitsArray CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn 632 +leaf CReverbEnvironmentEntry CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn 84 +leaf CShopItemDef CPersistContext::TransferVectorOfSubComponents 72 +leaf CShoveHaPennyRegionDef CPersistContext::TransferVectorOfSubComponents 48 +leaf CSimVoice CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferIn 28 +leaf CSkeletalMorphs CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferIn 20 +leaf CSoundBankEntry CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn 20 +leaf CSoundMap CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferIn 40 +leaf CSpecialEffectsStringMap CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferIn 16 +leaf CTCInventoryMap::CUIMiniMapMarker CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn,CPersistTraits::TransferOut +leaf CTCInventoryMap::CUIRegionInfo CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn,CPersistTraits::TransferOut +leaf CTCNoiseDef CPersistContext::TransferSubComponent 52 +leaf CTextureMorphs CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferIn 20 +leaf CTextureReplacementDef::CEntry CPersistContext::TransferVector,CPersistTraits::TransferIn +leaf CThingComponentSet CPersistContext::Transfer,CPersistTraits::TransferIn 8 +leaf CWeaponTrailGraphicSet CPersistContext::Transfer,CPersistContext::TransferMap,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn 8 +leaf CWideString CPersistContext::Transfer,CPersistTraits::TransferIn,CPersistTraits::TransferOut wchar_t 4 +leaf CWorld::ESaveType CPersistContext::Transfer +leaf CWoundMorphs CPersistContext::Transfer,CPersistTraits::TransferIn 20 +leaf EActionRegisteredType CPersistContext::Transfer,CPersistContext::TransferVector +leaf EClockHandType CPersistContext::Transfer +leaf EClothingSuitPart CPersistContext::Transfer +leaf ECombatCreatureType CPersistContext::Transfer +leaf ECombatSequenceInterruptionType CPersistContext::Transfer +leaf ECombatSequenceIsValidType CPersistContext::Transfer +leaf ECombatSequenceOnStartModuleType CPersistContext::Transfer +leaf ECombatSequenceOnStopModuleType CPersistContext::Transfer +leaf ECombatSequenceType CPersistContext::Transfer +leaf ECombatStrikeRecoilStyle CPersistContext::Transfer +leaf ECompositeBlendType CPersistContext::Transfer +leaf EContextSensitiveType CPersistContext::Transfer +leaf EControlledMovementType CPersistContext::Transfer +leaf EControllerType CPersistContext::Transfer +leaf ECreatureAbility CPersistContext::Transfer +leaf ECreatureGeneratorGenerateType CPersistContext::TransferVector +leaf ECreatureInteractionType CPersistContext::Transfer +leaf ECreatureType CPersistContext::Transfer +leaf ECrimeSeverity CPersistContext::Transfer +leaf EDamageAttribute CPersistContext::Transfer +leaf EDoorTriggerType CPersistContext::Transfer +leaf EEngineGraphicType CPersistContext::Transfer +leaf EEngineSprite2DFlag CPersistContext::Transfer,CPersistTraits::TransferBinaryIn +leaf EExpressionInventoryType CPersistContext::Transfer +leaf EFeatAttackType CPersistContext::Transfer +leaf EGameAction CPersistContext::TransferMap +leaf EGameEventType CPersistContext::Transfer +leaf EGameflowPosition CPersistContext::Transfer +leaf EGiftType CPersistContext::Transfer +leaf EHeroAbility CPersistContext::Transfer +leaf EHeroAttachableAppearanceModifierType CPersistContext::Transfer +leaf EHeroCentreDoorType CPersistContext::Transfer +leaf EHeroExperienceStatCategory CPersistContext::Transfer +leaf EHeroTitle CPersistContext::Transfer +leaf EHeroTrainingStatus CPersistContext::Transfer +leaf EHitLocationCodelet CPersistContext::Transfer +leaf EInputKey CPersistTraits::TransferIn +leaf EInventoryActionType CPersistContext::Transfer +leaf EInventoryObjectOrientation CPersistContext::Transfer +leaf ELightingChannel CPersistContext::Transfer +leaf EMessageEventType CPersistContext::Transfer +leaf EMinimapThemeType CPersistContext::Transfer +leaf EMouseButtonControl CPersistContext::Transfer +leaf EMusicSetType CPersistContext::Transfer +leaf ENavigatorType CPersistContext::TransferVector +leaf ENoiseType CPersistContext::Transfer +leaf EObjectAugmentationType CPersistContext::Transfer +leaf EOpinionAttitudeType CPersistContext::Transfer,CPersistContext::TransferMap,CPersistContext::TransferVector +leaf EOpinionDeedType CPersistContext::Transfer,CPersistTraits::TransferBinaryIn +leaf EPSwitchTriggerType CPersistContext::Transfer +leaf EPerceivedThingType CPersistContext::Transfer +leaf EPlaceInHeroSouls CPersistContext::Transfer +leaf EPointLightChannelEffect CPersistContext::Transfer +leaf EQuakeLength CPersistContext::Transfer +leaf EQuakeStrength CPersistContext::Transfer +leaf EReactionSpeechType CPersistContext::Transfer +leaf EReverbEnvironmentType CPersistContext::Transfer +leaf EScriptingStateGroups CPersistContext::Transfer +leaf ESex CPersistContext::Transfer +leaf ESwitchTriggerType CPersistContext::Transfer +leaf ETCInterfaceType CPersistContext::Transfer +leaf ETavernGameControlType CPersistContext::Transfer +leaf EThingCreatureProperty CPersistContext::Transfer +leaf ETrapTriggerType CPersistContext::Transfer +leaf ETrapType CPersistContext::Transfer +leaf ETutorialCategory CPersistContext::Transfer,CPersistContext::TransferVector +leaf EUIInventoryCategory CPersistContext::Transfer +leaf EVillageStanceToHero CPersistContext::Transfer +leaf EVillageTaskType CPersistContext::TransferVector +leaf EWallMountEffects CPersistContext::Transfer +leaf EWaterType CPersistContext::Transfer +leaf EWeaponClass CPersistContext::Transfer,CPersistContext::TransferVector +leaf EWeaponType CPersistContext::Transfer +leaf EWorldMapNameGraphic CPersistContext::Transfer +leaf EXboxControllerButton CPersistContext::Transfer +leaf NGraphicAppearance::CReplaceableMeshes CPersistContext::Transfer,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn +leaf NInventory::CItem CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn,CPersistTraits::TransferOut +leaf NInventory::EInventoryCategoryIdentifier CPersistContext::Transfer +leaf NInventory::EInventoryIdentifier CPersistContext::Transfer +leaf NParticleAttachment::CParticleAttachmentInfo CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn +leaf NScript::CQR_EscortTraderScript::EscortTraderQuestStage CPersistContext::Transfer +leaf NScript::CQR_EscortTrader_ManagerScript::EscortTraderManagerPhase CPersistContext::Transfer +leaf NScript::CQ_SunnyvaleMasterData::ArcheryOpenState CPersistContext::Transfer +leaf NScript::CV_AssassinAttacksScript::AA_LocationIdentifier CPersistContext::Transfer +leaf NScript::CV_ExposeMayorScript::PATH_ID CPersistContext::Transfer +leaf NScript::CV_ExposeMayorScript::STAGE_IN_PATH CPersistContext::Transfer +leaf NScript::CV_IntroductionToTrophiesScript::BriarRoseOpinion CPersistContext::Transfer +leaf NScript::CV_MurderTwistFinalScript::EAssassinState CPersistContext::Transfer +leaf NScript::CV_MurderTwistFinalScript::ESheriffState CPersistContext::Transfer +leaf NSpeechGainManager::CDialogueLayerDef CPersistContext::TransferVectorOfSubComponents +leaf NSpeechGainManager::EDialogueLayer CPersistContext::Transfer +leaf NUISystem::CUIStateDef CPersistContext::TransferVectorOfSubComponents +leaf NUISystem::EActionType CPersistContext::Transfer +leaf NUISystem::EStateChangeType CPersistContext::Transfer +leaf NUISystem::ETableExpansionTypes CPersistContext::Transfer +leaf NUISystem::ETextAlignement CPersistContext::Transfer +leaf NUISystem::EType CPersistContext::Transfer +leaf bool CPersistContext::Transfer,CPersistContext::TransferMap,CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn,CPersistTraits::TransferOut +leaf double CPersistContext::Transfer,CPersistTraits::TransferBinaryIn +leaf float CPersistContext::Transfer,CPersistContext::TransferMap,CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn,CPersistTraits::TransferOut +leaf int CPersistContext::Transfer,CPersistContext::TransferVector +leaf long CPersistContext::Transfer,CPersistContext::TransferMap,CPersistContext::TransferMultiMap,CPersistContext::TransferVector,CPersistTraits::TransferOut long +leaf short CPersistContext::Transfer,CPersistTraits::TransferBinaryIn +leaf signed char CPersistContext::Transfer +leaf std::list > CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn container-count +leaf std::list > CPersistContext::GetDefaultVal,CPersistContext::Transfer,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferBinaryOut,CPersistTraits::TransferIn pointer+container-count +leaf std::pair CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn,CPersistTraits::TransferOut long +leaf std::pair CPersistContext::TransferVector,CPersistTraits::TransferIn pointer+long +leaf std::pair CPersistContext::TransferMap,CPersistTraits::TransferOut long +leaf unsigned __int64 CPersistContext::Transfer,CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferOut +leaf unsigned char CPersistContext::Transfer,CPersistTraits::TransferBinaryIn,CPersistTraits::TransferIn +leaf unsigned long CPersistContext::Transfer,CPersistContext::TransferMap,CPersistContext::TransferVector,CPersistTraits::TransferBinaryIn long +class CAICreatureWillPowerIndicatorDef Transfer 44 0 0 0 +class CAIScratchpadDef Transfer 156 0 8 0 +class CANavQuadTreeNode SaveToFile 16 0 0 0 +class CAbilityButtonGuiDef Transfer 96 0 1 0 +class CAbilityDef Transfer 44 0 0 0 +class CAbilityTrackDef Transfer 88 0 1 0 +class CAbilityTrackLevelDef Transfer 76 0 2 0 +class CActionInputControl TransferBinaryIn 28 0 0 0 +class CActionUseDef Transfer 52 0 0 0 +class CActivateQuestDef Transfer 48 0 0 0 +class CAnimComponentCombatComboChain TransferBinaryIn,TransferBinaryOut 16 0 2 0 +class CAnimComponentCombatFlourishTargetOffset TransferBinaryIn,TransferBinaryOut 17 0 0 0 +class CAnimComponentCombatHandedness TransferBinaryIn,TransferBinaryOut 16 0 0 0 +class CAnimComponentCombatMisc TransferBinaryIn,TransferBinaryOut 10 0 0 0 +class CAnimComponentCombatRecoil TransferBinaryIn,TransferBinaryOut 12 0 1 0 +class CAnimComponentDelayTime TransferBinaryIn,TransferBinaryOut 12 0 1 0 +class CAnimComponentFlags TransferBinaryIn,TransferBinaryOut 12 0 1 0 +class CAnimComponentFollowingOnAnim TransferBinaryIn,TransferBinaryOut 12 0 0 0 +class CAnimComponentFollowingOnAnimFilter TransferBinaryIn,TransferBinaryOut 12 0 0 0 +class CAnimComponentSetCreatureMode TransferBinaryIn,TransferBinaryOut 12 0 0 0 +class CAnimComponentStrikeResponseAnim TransferBinaryIn,TransferBinaryOut 12 0 0 0 +class CAnimComponentStrikeSpeedMultiplier TransferBinaryIn,TransferBinaryOut 12 0 0 0 +class CAnimComponentTransitionInTime TransferBinaryIn,TransferBinaryOut 12 0 1 0 +class CAnimatingObjectDef Transfer 76 0 0 0 +class CAnimationEntry TransferBinaryIn,TransferBinaryOut 16 0 1 0 +class CAnimationSet TransferBinaryIn,TransferBinaryOut 36 0 3 0 +class CAppearanceDef Transfer 124 0 0 0 +class CAppearanceModifierDef Transfer 120 0 3 0 +class CAppearanceModifierGraphics TransferBinaryIn,TransferBinaryOut 16 0 0 0 +class CAppearanceModifierScalingDef Transfer 76 0 0 0 +class CAreaOfEffectAttackDef Transfer 80 0 2 0 +class CArenaCreatureDef Transfer 56 0 2 0 +class CArenaRoundDef Transfer 60 0 1 0 +class CArenaWaveDef Transfer 64 0 1 0 +class CArmourDef Transfer 124 0 2 0 +class CAttackPatternDef Transfer 80 0 0 0 +class CAugmentationDef Transfer 76 0 4 0 +class CBalverineBattleDef Transfer 76 0 0 0 +class CBase TransferPersistantBaseData,TransferSubComponentsFrom 200 1 0 0 +class CBedDef Transfer 60 0 0 0 +class CBettingDef Transfer 88 0 8 0 +class CBlendedParticleEffectSet TransferBinaryIn,TransferBinaryOut 16 0 2 0 +class CBlurredAttitudesDef Transfer 80 0 0 0 +class CBoastingPodiumDef Transfer 44 0 0 0 +class CBonusItemDef Transfer 116 0 7 0 +class CBossDef Transfer 96 0 1 0 +class CBrainDef Transfer 112 0 0 0 +class CBriarRoseDef Transfer 84 0 3 0 +class CBuyHouseDef Transfer 38 0 0 0 +class CBuyableHouseDef Transfer 84 0 2 0 +class CCRCSymbolMap LoadFromFile 44 0 0 0 +class CCameraCollisionDef Transfer 44 0 1 0 +class CCameraManagerDef Transfer 128 0 3 0 +class CCameraManagerSetDef Transfer 80 0 4 0 +class CCameraModeDef Transfer 220 0 3 0 +class CCardDef Transfer 72 0 2 0 +class CCardPositionsDef Transfer 80 0 0 0 +class CCarriedReadableDef Transfer 48 0 0 0 +class CCarrySlotDef Transfer 56 0 1 0 +class CCarryableDef Transfer 80 0 4 0 +class CCarryingDef Transfer 60 0 0 0 +class CCharString LoadFromFile,SaveToFile 4 1 0 0 +class CChecksumCacheFile Save 28 0 1 2 +class CChestDef Transfer 60 0 3 0 +class CClockDef Transfer 60 0 0 0 +class CCoinGameObstacleDef Transfer 44 0 0 0 +class CCombatAbilityAttackBase Transfer 72 0 0 0 +class CCombatAbilityBlockDefBase Transfer 92 0 0 0 +class CCombatAbilityData TransferBinaryIn,TransferBinaryOut 24 0 1 0 +class CCombatAbilityStrafeDef Transfer 68 0 0 0 +class CCombatAbilityUseProjectileWeaponDef Transfer 72 0 0 0 +class CCombatDialogueDef Transfer 144 0 17 0 +class CCombatSequenceDef Transfer 112 0 1 0 +class CCombatTypeDef Transfer 192 0 1 0 +class CConfigOptionsDefaultsDef Transfer 120 0 6 0 +class CContainerRewardHeroDef Transfer 56 0 0 0 +class CContextSensitiveItemDef Transfer 60 0 4 0 +class CControlsDef Transfer 88 0 0 0 +class CConversation Transfer 108 0 1 0 +class CCoopSpiritDef Transfer 60 0 0 0 +class CCrateStackDef Transfer 48 0 2 0 +class CCreatureAbilityDef Transfer 68 0 0 0 +class CCreatureDef Transfer 252 0 19 0 +class CCreatureGenerationFamilyDef Transfer 104 0 2 0 +class CCreatureGeneratorDef Transfer 68 0 2 0 +class CCreatureModeDef Transfer 68 0 3 0 +class CCreatureNavigationDef Transfer 56 0 0 0 +class CCreatureStatsDef Transfer 72 0 5 0 +class CCutsceneDef Transfer 192 0 0 0 +class CDecapitationDef Transfer 48 0 2 0 +class CDefString TransferBinaryIn 4 0 1 0 +class CDegradableDef Transfer 64 0 0 0 +class CDialogueLayerDef Transfer 64 0 0 0 +class CDoorDef Transfer 60 0 2 0 +class CDragonActionHoverDef Transfer 44 0 0 0 +class CDragonActionNapalmDef Transfer 56 0 0 0 +class CDragonActionSwoopDef Transfer 100 0 1 0 +class CDrunkennessDef Transfer 44 0 0 0 +class CEditMapBrush LoadFromFile,SaveToFile 64 0 0 0 +class CEditWorldMap LoadFromFile,SaveToFile 332 1 2 0 +class CEnemyDef Transfer 44 0 1 0 +class CEngineDef Transfer 132 0 8 0 +class CEngineLandscapePatch Save 72 4 2 0 +class CEngineLocalDetailGeneratorDef Transfer 80 0 0 0 +class CEngineLocalDetailLayerDef Transfer 96 0 0 0 +class CEngineLocalDetailObjectDef Transfer 140 0 4 0 +class CEngineMapDependencyChecker Load,Save 20 0 1 0 +class CEngineProfileLog Save 1592 1 4 1 +class CEngineStaticMapEdgeHeights Load,Save 232 0 2 0 +class CEngineTexturePalette Load,Save 28 1 1 0 +class CEngineThemeDef Transfer 168 0 16 0 +class CEngineThemeGroupDef Transfer 62 0 0 0 +class CEngineVideoOptionsDef Transfer 124 0 2 0 +class CEngineWaterBackgroundSubPatch Load,Save 96 5 3 0 +class CEntitySoundDef Transfer 92 0 1 0 +class CEnvironmentDef Transfer 312 0 45 0 +class CEnvironmentThemeDaySetDef Transfer 116 0 1 0 +class CEnvironmentThemeDef Transfer 200 0 6 0 +class CExperienceDef Transfer 92 0 1 0 +class CExplodingObjectDef Transfer 52 0 2 0 +class CExplosionDef Transfer 116 0 5 0 +class CExplosionRing TransferBinaryIn,TransferBinaryOut 16 0 1 0 +class CExplosiveTrailDef Transfer 76 0 1 0 +class CExpressionDef Transfer 148 0 2 0 +class CExpressionSet TransferBinaryIn,TransferBinaryOut 20 0 0 0 +class CExpressionSubDef Transfer 44 0 1 0 +class CFactionDef Transfer 96 0 0 0 +class CFeatDef Transfer 100 0 4 0 +class CFireHeartPatternDef Transfer 60 0 0 0 +class CFireballSpellLevelDef Transfer 44 0 1 0 +class CFireheartMinigameDef Transfer 60 0 3 0 +class CFishDef Transfer 88 0 2 0 +class CFishingDef Transfer 128 0 3 0 +class CFishingRodDef Transfer 60 0 0 0 +class CFlammableDef Transfer 80 0 1 0 +class CFrontEndDef Transfer 92 0 3 0 +class CGameTimeManager Load,Save 64 2 2 0 +class CGiftDef Transfer 48 0 0 0 +class CGlobalDef Transfer 352 0 38 0 +class CGoldDef Transfer 44 0 1 0 +class CGraphicHeader Load,Save 28 0 2 0 +class CGuardDef Transfer 80 0 10 0 +class CGuiVarTransferBool TransferFromGui,TransferToGui 20 1 0 0 +class CGuiVarTransferDefIndex TransferFromGui,TransferToGui 28 2 1 0 +class CGuiVarTransferDefIndexList TransferFromGui,TransferToGui 36 1 0 0 +class CGuiVarTransferExclusiveOptions TransferFromGui,TransferToGui 52 0 2 0 +class CGuiVarTransferFloat TransferFromGui,TransferToGui 32 1 0 0 +class CGuiVarTransferSLONG TransferFromGui,TransferToGui 32 1 4 0 +class CGuiVarTransferString TransferFromGui,TransferToGui 20 1 0 0 +class CGuiVarTransferStringList TransferFromGui,TransferToGui 36 1 0 0 +class CGuiVarTransferSymbolVal TransferFromGui,TransferToGui 28 2 1 0 +class CGuiVarTransferULONG TransferFromGui,TransferToGui 32 1 4 0 +class CGuiWindowManager Load,Save 136 6 1 0 +class CGuildMasterDef Transfer 48 0 0 0 +class CHairCardDef Transfer 44 0 1 0 +class CHasNameDef Transfer 52 0 3 0 +class CHeroAbilityDef Transfer 172 0 8 0 +class CHeroCentreDef Transfer 0 0 0 +class CHeroCombatDef Transfer 372 0 2 0 +class CHeroDef Transfer 48 0 1 0 +class CHeroExperienceDef Transfer 224 0 0 0 +class CHeroMarriageDef Transfer 200 0 9 0 +class CHeroMorphDef Transfer 120 0 0 0 +class CHeroPostcardGeneratorDef Transfer 96 0 0 0 +class CHeroSoulsCreatureDef Transfer 52 0 1 0 +class CHeroSoulsRoundDef Transfer 60 0 1 0 +class CHeroSoulsWaveDef Transfer 60 0 1 0 +class CHeroSpecialMovementDef Transfer 56 0 0 0 +class CHeroStatsDef Transfer 356 0 19 0 +class CHeroSuitDef Transfer 56 0 0 0 +class CHeroTitleDef Transfer 80 0 8 0 +class CHighlightItemDef Transfer 72 0 2 0 +class CHitLocationDef Transfer 128 0 5 0 +class CHitLocationsDef Transfer 56 0 0 0 +class CIdleSchedulerDef Transfer 80 0 0 0 +class CInputEventPackage Save 52 0 1 0 +class CInterestingToVillagersDef Transfer 44 0 0 0 +class CInventoryCategoryDef Transfer 92 0 3 0 +class CInventoryDef Transfer 2656 0 142 0 +class CInventoryItemDef Transfer 112 0 7 0 +class CJackDragonDef Transfer 120 0 4 0 +class CJackOfBladesBattleDef Transfer 132 0 1 0 +class CKickableDef Transfer 84 0 2 0 +class CKrakenDef Transfer 128 0 6 0 +class CKrakenTentacleDef Transfer 96 0 0 0 +class CLandscapeBackgroundPatch Load,Save 284 9 1 0 +class CLandscapeBackgroundSubNodeSplitBranch Load 484 0 0 0 +class CLandscapeLayerMesh Save 84 9 7 0 +class CLensFlareElementDef Transfer 80 0 1 0 +class CLightDef Transfer 92 0 1 0 +class CLightningDef Transfer 92 0 2 0 +class CLightningOrbDef Transfer 60 0 2 0 +class CLocalDetailObjectCollectionType Load,Save 84 3 4 0 +class CLocalDetailPrimitiveMesh Load,Save 116 0 0 0 +class CLocalDetailPrimitiveMeshZSpriteBatch Load,Save 80 2 1 0 +class CLocalDetailPrimitiveRepeatedMesh Load,Save 96 5 2 0 +class CLookDef Transfer 88 0 0 0 +class CManager Load,Save 256 17 5 1 +class CMap LoadFromFile,SaveToFile 7504 3 9 0 +class CMaterialDef Transfer 180 0 2 0 +class CMazeBattleDef Transfer 100 0 2 0 +class CMeleeCombatAbilityDef Transfer 136 0 4 0 +class CMeleeCombatKnockdownEffects Transfer 96 0 5 0 +class CMessageEventDef Transfer 84 0 3 0 +class CMultiStaticMeshDef Transfer 56 0 0 0 +class CMultiStaticMeshEntryDef Transfer 56 0 1 0 +class CNavBlockedQuadTreeNode SaveToFile 0 0 0 +class CNavNavigableLeafNode SaveToFile 32 1 0 0 +class CNavQuadTree LoadFromFile,SaveToFile 176 0 1 0 +class CNavQuadTreeNode SaveToFile 32 4 0 0 +class CNavSwitchableLeafNode SaveToFile 52 0 0 0 +class CNymphDef Transfer 80 0 3 0 +class CObject Load 40 2 1 0 +class CObjectAugmentationsDef Transfer 160 0 9 0 +class CObjectFamilyDef Transfer 80 0 0 0 +class CObjectTypeCollection Load,Save 28 5 0 0 +class CObjectTypeCollectionPalette Load,Save 16 0 0 0 +class COccupiableDef Transfer 44 0 1 0 +class COpinionDeedEffectsDef Transfer 148 0 5 0 +class COpinionDeedMaskDef Transfer 80 0 0 0 +class COpinionDeedReactionDef Transfer 88 0 1 0 +class COpinionOfHeroDef Transfer 60 0 3 0 +class COpinionPersonalityDef Transfer 92 1 0 0 +class COpinionPersonalityTraits TransferBinaryIn,TransferBinaryOut 36 0 0 0 +class COpinionReactionManagerDef Transfer 860 2 0 0 +class COpinionReactionMaskDef Transfer 80 0 0 0 +class COpinionSet TransferBinaryIn 16 0 0 0 +class COpinionSourceDef Transfer 216 0 1 0 +class COpinionTransientOffset TransferBinaryIn 24 0 3 0 +class COracleMinigameDef Transfer 92 0 10 0 +class COverheadDisplayDef Transfer 40 0 0 0 +class CParticleAttacherDef Transfer 56 0 0 0 +class CParticleEmitterDatabase Load,Save,Serialise 88 0 1 0 +class CParticleMorphs TransferBinaryIn,TransferBinaryOut 20 0 0 0 +class CPatchTesselationEdgeStrip Load,Save 44 0 0 0 +class CPerceivedThingDef Transfer 80 0 0 0 +class CPersistContext PersistError,TransferObjectLoadText,TransferObjectSaveText,TransferSubComponentLoadText,TransferSubComponentSaveText,TransferVectorLoadText,TransferVectorOfSubComponentsLoadText,TransferVectorSaveText 44 8 0 0 +class CPersistTraitsSLONG TransferIn,TransferOut 0 0 0 +class CPhysicalPrimitiveCylinder TransferBinaryIn,TransferBinaryOut 16 0 0 0 +class CPhysicalPrimitiveMesh TransferBinaryIn,TransferBinaryOut 0 0 0 +class CPhysicalPrimitiveSphere TransferBinaryIn,TransferBinaryOut 12 0 0 0 +class CPhysicsDef Transfer 68 0 1 0 +class CPlayerDef Transfer 72 0 1 0 +class CPlayerGuiDef Transfer 2896 0 219 0 +class CPlayerInventoryDef Transfer 136 0 1 0 +class CPlayerMovementDef Transfer 128 0 3 0 +class CPrizeScoreDef Transfer 48 0 1 0 +class CQuestCardDef Transfer 124 0 9 0 +class CQuestManager TransferBoasts 276 4 5 0 +class CQuickAccessItemButtonGuiDef Transfer 84 0 1 0 +class CRandomAppearanceMorph TransferBinaryIn,TransferBinaryOut 76 0 1 0 +class CReactionFrequencyTraits_ControlledCount TransferBinaryIn,TransferBinaryOut 44 1 3 0 +class CReactionFrequencyTraits_LinearTime TransferBinaryIn,TransferBinaryOut 16 0 1 0 +class CReactionFrequencyTraits_PeriodicFrequency TransferBinaryIn,TransferBinaryOut 32 0 5 0 +class CReactionMatch TransferBinaryIn,TransferBinaryOut 8 0 0 0 +class CReactionMatch_AsymmetricPyramid TransferBinaryIn,TransferBinaryOut 40 0 0 0 +class CReactionMatch_MRPyramid TransferBinaryIn,TransferBinaryOut 52 0 0 0 +class CReactionMatch_SecondaryAxisCentredMRShifted TransferBinaryIn,TransferBinaryOut 36 0 0 0 +class CReactionMatch_SecondaryAxisMRShifted TransferBinaryIn,TransferBinaryOut 36 0 0 0 +class CReactionMatch_Sphere TransferBinaryIn,TransferBinaryOut 28 0 0 0 +class CReadableDef Transfer 38 0 0 0 +class CRegion TransferGameState 100 0 0 0 +class CRegionDef Transfer 208 0 11 0 +class CRegionScriptDef Transfer 80 0 4 0 +class CReplaceableMeshDef Transfer 56 0 0 0 +class CResurrectionItemDef Transfer 44 0 1 0 +class CRumbleDef Transfer 72 0 0 0 +class CScorpionKingBattleDef Transfer 100 0 1 0 +class CScriptDef Transfer 4436 0 548 0 +class CShipDef Transfer 68 0 3 0 +class CShopDef Transfer 224 0 10 0 +class CShopItemDef Transfer 72 0 1 0 +class CShoveHaPennyRegionDef Transfer 48 0 0 0 +class CSimBuildingDef Transfer 76 0 3 0 +class CSimVoice TransferBinaryIn,TransferBinaryOut 28 0 0 0 +class CSimVoicesDef Transfer 92 0 0 0 +class CSkeletalMorphDef Transfer 56 0 0 0 +class CSkeletalMorphs TransferBinaryIn,TransferBinaryOut 20 0 0 0 +class CSkyDef Transfer 136 0 10 0 +class CSmashableDef Transfer 48 0 1 0 +class CSmokeGeneratorDef Transfer 48 0 1 0 +class CSnowTrollDef Transfer 96 0 3 0 +class CSoundAtmospheresDef Transfer 56 0 0 0 +class CSoundDef Transfer 156 0 0 0 +class CSoundMap TransferBinaryIn,TransferBinaryOut 40 0 0 0 +class CSoundThemeDef Transfer 108 0 2 0 +class CSpecialAbilitiesAssassinRushDef Transfer 148 0 2 0 +class CSpecialAbilitiesBattleChargeDef Transfer 184 0 0 0 +class CSpecialAbilitiesBerserkDef Transfer 468 0 3 0 +class CSpecialAbilitiesBulletTimeDef Transfer 428 0 1 0 +class CSpecialAbilitiesBurntEffectDef Transfer 92 0 0 0 +class CSpecialAbilitiesCreatureTintDef Transfer 88 0 0 0 +class CSpecialAbilitiesDrainLifeDef Transfer 132 0 0 0 +class CSpecialAbilitiesDrunkennessDef Transfer 84 0 2 0 +class CSpecialAbilitiesElectrocutedEffectDef Transfer 120 0 0 0 +class CSpecialAbilitiesEnflameDef Transfer 176 0 0 0 +class CSpecialAbilitiesFireballSpellDef Transfer 92 0 0 0 +class CSpecialAbilitiesForcePushDataDef Transfer 44 0 0 0 +class CSpecialAbilitiesForcePushDef Transfer 180 0 2 0 +class CSpecialAbilitiesGhostSwordDef Transfer 92 0 0 0 +class CSpecialAbilitiesHealLifeDef Transfer 112 0 0 0 +class CSpecialAbilitiesLightningSpellDef Transfer 364 0 0 0 +class CSpecialAbilitiesMultiArrowDef Transfer 140 0 2 0 +class CSpecialAbilitiesMultiStrikeDef Transfer 180 0 0 0 +class CSpecialAbilitiesPhysicalShieldDef Transfer 364 0 0 0 +class CSpecialAbilitiesSummonSpellDef Transfer 120 0 1 0 +class CSpecialAbilitiesThunderLightningStormDef Transfer 108 0 1 0 +class CSpecialAbilitiesTurncoatSpellDef Transfer 144 0 0 0 +class CSpecialAbilitiesUnholyPowerDef Transfer 312 0 0 0 +class CSpecialEffectsDef Transfer 56 0 0 0 +class CSpotLightDef Transfer 68 0 0 0 +class CStealthDef Transfer 72 0 0 0 +class CStockItemDef Transfer 64 0 3 0 +class CSummonDef Transfer 76 0 7 0 +class CSummonableCreatureDef Transfer 48 0 0 0 +class CSummonerDef Transfer 92 0 3 0 +class CSymbolMap LoadFromFile 44 0 0 0 +class CTCBase Serialise 12 1 0 0 +class CTCNoiseDef Transfer 52 0 0 0 +class CTCVolumeContainmentTrackerDef Transfer 48 0 0 0 +class CTGAFile Load 12 0 1 0 +class CTargetingDef Transfer 148 0 1 0 +class CTattooDef Transfer 96 0 5 0 +class CTavernDef Transfer 44 0 1 0 +class CTavernGameCardBaseDef Transfer 140 0 2 0 +class CTavernGameCoinBaseDef Transfer 68 0 2 0 +class CTavernGameCoinGolfDef Transfer 92 0 2 0 +class CTavernGameDef Transfer 424 0 62 0 +class CTavernGameShoveHaPennyDef Transfer 520 0 3 0 +class CTavernGameSpotTheAdditionDef Transfer 152 0 3 0 +class CTavernTableDef Transfer 39 0 0 0 +class CTeleporterDef Transfer 52 0 1 0 +class CTexture LoadFromFile,SaveToFile 8 1 0 0 +class CTextureMorphs TransferBinaryIn,TransferBinaryOut 20 0 0 0 +class CTextureReplacementDef Transfer 56 0 0 0 +class CThing Save 168 7 2 0 +class CThingAICreature Save 528 1 2 0 +class CThingBaseDef Transfer 116 0 2 0 +class CThingBuildingDef Transfer 168 0 5 0 +class CThingComponentSet TransferBinaryIn,TransferBinaryOut 8 0 0 0 +class CThingCreatureBase Save 392 2 5 0 +class CThingCreatureDef Transfer 340 0 15 0 +class CThingDrainLifeShotDef Transfer 60 0 0 0 +class CThingHolySiteDef Transfer 132 0 0 0 +class CThingManager LoadFromFile 272 8 3 1 +class CThingMarkerDef Transfer 140 0 1 0 +class CThingMultiArrowShotDef Transfer 84 0 3 0 +class CThingNoiseDef Transfer 168 0 0 0 +class CThingObjectDef Transfer 160 0 5 0 +class CThingPhysical Save 216 0 1 0 +class CThingPhysicalSwitch Save 240 0 0 0 +class CThingPhysicalSwitchDef Transfer 152 0 1 0 +class CThingShotDef Transfer 176 0 9 0 +class CThingSwitch Save 256 0 1 0 +class CThingSwitchDef Transfer 120 0 0 0 +class CThingTrackNode Save 264 0 0 0 +class CThingVillageDef Transfer 132 0 0 0 +class CThunderBattleDef Transfer 80 0 3 0 +class CTimeAppearanceFadeDef Transfer 56 0 0 0 +class CTrapDef Transfer 104 0 5 0 +class CTrollBattleDef Transfer 100 0 3 0 +class CTrophyDef Transfer 100 0 2 0 +class CTurncoatDef Transfer 88 0 3 0 +class CUIDef Transfer 604 0 18 2 +class CUIIconsDef Transfer 192 0 32 0 +class CUILocaleGraphicsDef Transfer 100 0 1 0 +class CUIMiscThingsDef Transfer 620 0 10 70 +class CUIStateDef Transfer 132 0 2 0 +class CUserProfileManager Load,Save 324 1 10 5 +class CVillageDef Transfer 300 0 26 0 +class CVillageMemberDef Transfer 38 0 0 0 +class CVillagePeopleDef Transfer 204 0 29 0 +class CVillagerInteractionsDef Transfer 76 0 0 0 +class CVolumeTexture LoadFromFile,SaveToFile 16 2 1 0 +class CWallMountEffectsDef Transfer 52 0 1 0 +class CWaspQueenBattleDef Transfer 68 0 1 0 +class CWaterPatchMesh Load,Save 32 3 0 0 +class CWaterSeaStaticMapInfoBlock Load,Save 12 0 3 0 +class CWaterStaticMapInfoBlock Load,Save 8 0 2 0 +class CWeaponDef Transfer 244 0 8 0 +class CWhisperBattleDef Transfer 72 0 2 0 +class CWideString LoadFromFile,SaveToFile 4 1 0 0 +class CWifeDef Transfer 44 0 1 0 +class CWillResponseDef Transfer 45 0 0 0 +class CWorld Load 440 5 8 2 +class CWorldMap LoadFromFile,SaveToFile 252 2 5 0 +class CWound TransferBinaryIn,TransferBinaryOut 68 0 6 0 +class CWoundMorphs TransferBinaryIn,TransferBinaryOut 20 0 0 0 diff --git a/docs/modern/PLATFORM_SURFACE.md b/docs/modern/PLATFORM_SURFACE.md new file mode 100644 index 0000000000..13d17e6850 --- /dev/null +++ b/docs/modern/PLATFORM_SURFACE.md @@ -0,0 +1,547 @@ +# Retail platform surface + +Generated by `tools/modern/platform_surface.py` from the retail `Fable.exe` import table. Do not +hand-edit; re-run the tool. Plan and context: [MODERNIZATION](MODERNIZATION.md). + +A reference is an absolute IAT-slot address found in an executable section; it is attributed to +the nearest preceding function start in `rebuild/manifest/functions.tsv`. Every function listed +here must sit behind a platform service before the x64 step (see MODERNIZATION, step 3). + +COM APIs undercount here: `d3d9.dll` shows one import (`Direct3DCreate9`) and `dinput8.dll` one +(`DirectInput8Create`) because everything after creation is a vtable call on the returned +interface. The real D3D9 surface is the `IDirect3DDevice9` call-site census (open task in +MODERNIZATION, step 4). + +## Summary + +| DLL | Area | Imports | Code refs | Functions | Modern replacement | +|---|---|---:|---:|---:|---| +| `kernel32.dll` | OS | 120 | 1250 | 518 | Platform file/thread/sync/time layer (Win32 now, POSIX later) | +| `msvcr71.dll` | CRT | 123 | 423 | 251 | Modern CRT (MSVC / glibc); CRT-shaped retail code becomes ordinary C++ standard library use | +| `user32.dll` | Window | 75 | 216 | 45 | Platform window/message service | +| `wsock32.dll` | Network | 21 | 71 | 23 | Drop or platform sockets (cut co-op only) | +| `winmm.dll` | Timing/Audio | 22 | 60 | 29 | Platform clock (QueryPerformanceCounter / clock_gettime) | +| `ole32.dll` | Video/COM | 7 | 57 | 31 | Drops with DirectShow; nothing else should need COM | +| `advapi32.dll` | Config | 9 | 50 | 22 | Registry -> config file in the user profile directory | +| `msvcp71.dll` | CRT | 3 | 18 | 12 | Modern C++ standard library | +| `imm32.dll` | Input/IME | 17 | 17 | 13 | Platform text-input service | +| `d3dx9_25.dll` | Graphics | 11 | 11 | 10 | Own math/texture/shader-compile code; shaders re-authored in HLSL, DXC -> DXIL + SPIR-V | +| `gdi32.dll` | Window | 5 | 11 | 5 | Platform window service (GDI use is cursor/font fallback; verify) | +| `shell32.dll` | OS | 2 | 5 | 4 | Platform paths (known folders / XDG) | +| `oleaut32.dll` | Video/COM | 2 | 4 | 2 | Drops with DirectShow | +| `version.dll` | OS | 3 | 3 | 3 | Drop (driver/version probing) | +| `d3d9.dll` | Graphics | 1 | 1 | 1 | RHI: D3D12 + Vulkan backends (hand-written); D3D9 backend kept as reference | +| `dinput8.dll` | Input | 1 | 1 | 1 | Platform input service: raw input + XInput/GameInput on Windows, evdev later | + +## Loaded at runtime (not in the import table) + +| Area | Library | String evidence found in Fable.exe | Modern replacement | +|---|---|---|---| +| Audio | OpenAL (Creative `wrap_oal` DirectSound3D wrapper) | `OpenAL32.dll`, `wrap_oal.dll`, `alBufferData`, `alDistanceModel` | OpenAL Soft (same API, x64 + Linux); `wrap_oal`/DirectSound3D path drops | +| Video | DirectShow via COM | `CoCreateInstance`, `.wmv` | ffmpeg (libavformat/libavcodec) decoding the retail .wmv files | +| Config | ConfigDetect.dll (hardware tier probe) | `ConfigDetect.dll` | Own capability probe on the RHI device | +| Text | strings.dll (localised launcher/error strings) | `strings.dll` | Plain data file | + +## Imports by DLL + +### `kernel32.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `LeaveCriticalSection` | 246 | 145 | +| `EnterCriticalSection` | 178 | 145 | +| `GetProcAddress` | 88 | 71 | +| `CloseHandle` | 52 | 45 | +| `SetEvent` | 43 | 40 | +| `GetTickCount` | 31 | 21 | +| `Sleep` | 30 | 26 | +| `InitializeCriticalSection` | 26 | 26 | +| `DeleteCriticalSection` | 25 | 25 | +| `WaitForSingleObject` | 18 | 16 | +| `lstrlenW` | 17 | 12 | +| `InterlockedIncrement` | 16 | 14 | +| `QueryPerformanceCounter` | 15 | 15 | +| `FreeLibrary` | 14 | 11 | +| `ResetEvent` | 14 | 13 | +| `GetLastError` | 13 | 12 | +| `CreateFileW` | 12 | 11 | +| `SetFilePointer` | 12 | 12 | +| `CreateEventW` | 10 | 10 | +| `GetFileSize` | 10 | 9 | +| `InterlockedDecrement` | 10 | 10 | +| `LoadLibraryA` | 10 | 10 | +| `MultiByteToWideChar` | 10 | 6 | +| `VirtualFree` | 10 | 10 | +| `GlobalFree` | 9 | 9 | +| `SetThreadPriority` | 9 | 8 | +| `WideCharToMultiByte` | 9 | 7 | +| `lstrlenA` | 9 | 8 | +| `CreateThread` | 7 | 7 | +| `GetFileAttributesW` | 7 | 7 | +| `VirtualAlloc` | 7 | 7 | +| `WaitForSingleObjectEx` | 7 | 7 | +| `GetCurrentThread` | 6 | 5 | +| `GetCurrentThreadId` | 6 | 5 | +| `GetSystemTime` | 6 | 6 | +| `ReleaseSemaphore` | 6 | 6 | +| `SleepEx` | 6 | 6 | +| `WaitForMultipleObjects` | 6 | 5 | +| `CreateSemaphoreW` | 5 | 5 | +| `DeleteFileW` | 5 | 5 | +| `GetProcessHeap` | 5 | 4 | +| `QueryPerformanceFrequency` | 5 | 5 | +| `TryEnterCriticalSection` | 5 | 5 | +| `lstrcpyW` | 5 | 5 | +| `CreateEventA` | 4 | 3 | +| `CreateFileA` | 4 | 4 | +| `DeviceIoControl` | 4 | 4 | +| `FileTimeToSystemTime` | 4 | 4 | +| `FindResourceExA` | 4 | 4 | +| `FlushFileBuffers` | 4 | 4 | +| `GetCurrentDirectoryA` | 4 | 4 | +| `GetCurrentProcess` | 4 | 4 | +| `GetVersionExA` | 4 | 4 | +| `GlobalAlloc` | 4 | 4 | +| `GlobalMemoryStatus` | 4 | 4 | +| `LoadLibraryW` | 4 | 3 | +| `LoadResource` | 4 | 4 | +| `LockResource` | 4 | 4 | +| `OutputDebugStringA` | 4 | 3 | +| `ReadFile` | 4 | 4 | +| `ReadFileEx` | 4 | 4 | +| `SetEndOfFile` | 4 | 4 | +| `lstrcmpW` | 4 | 4 | +| `lstrcpyA` | 4 | 3 | +| `lstrcpynW` | 4 | 4 | +| `CreateSemaphoreA` | 3 | 3 | +| `ExitProcess` | 3 | 3 | +| `FindClose` | 3 | 3 | +| `FindFirstFileA` | 3 | 3 | +| `GetCommandLineW` | 3 | 3 | +| `GetFileAttributesExW` | 3 | 3 | +| `GetModuleHandleA` | 3 | 2 | +| `GetModuleHandleW` | 3 | 3 | +| `GetSystemDefaultLangID` | 3 | 3 | +| `GetSystemDirectoryA` | 3 | 3 | +| `HeapFree` | 3 | 3 | +| `InterlockedExchange` | 3 | 3 | +| `VirtualQuery` | 3 | 3 | +| `WriteFileEx` | 3 | 3 | +| `lstrcatA` | 3 | 3 | +| `lstrcatW` | 3 | 3 | +| `CompareStringA` | 2 | 2 | +| `ConvertThreadToFiber` | 2 | 2 | +| `CreateMutexW` | 2 | 2 | +| `DebugBreak` | 2 | 2 | +| `DuplicateHandle` | 2 | 2 | +| `EnumResourceNamesA` | 2 | 2 | +| `FileTimeToLocalFileTime` | 2 | 2 | +| `FormatMessageW` | 2 | 2 | +| `GetCPInfoExA` | 2 | 2 | +| `GetCurrentProcessId` | 2 | 2 | +| `GetDateFormatW` | 2 | 2 | +| `GetDiskFreeSpaceExW` | 2 | 2 | +| `GetFileInformationByHandle` | 2 | 2 | +| `GetFileSizeEx` | 2 | 2 | +| `GetFileTime` | 2 | 2 | +| `GetLocaleInfoA` | 2 | 2 | +| `GetModuleFileNameW` | 2 | 2 | +| `GetStartupInfoA` | 2 | 2 | +| `GetSystemInfo` | 2 | 2 | +| `GetSystemTimeAsFileTime` | 2 | 2 | +| `GetThreadPriority` | 2 | 2 | +| `GetTimeFormatW` | 2 | 2 | +| `GetUserDefaultLCID` | 2 | 2 | +| `GetVersion` | 2 | 2 | +| `GetVersionExW` | 2 | 2 | +| `HeapAlloc` | 2 | 2 | +| `HeapSize` | 2 | 2 | +| `IsDBCSLeadByteEx` | 2 | 2 | +| `LocalFree` | 2 | 2 | +| `MulDiv` | 2 | 2 | +| `OpenMutexW` | 2 | 2 | +| `OutputDebugStringW` | 2 | 2 | +| `RemoveDirectoryW` | 2 | 2 | +| `SetFileAttributesW` | 2 | 2 | +| `SetFileTime` | 2 | 2 | +| `SetPriorityClass` | 2 | 2 | +| `SystemTimeToFileTime` | 2 | 2 | +| `WriteFile` | 2 | 2 | +| `lstrcmpiW` | 2 | 2 | + +### `msvcr71.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `free` | 50 | 36 | +| `memmove` | 39 | 26 | +| `malloc` | 37 | 22 | +| `floor` | 26 | 16 | +| `calloc` | 25 | 20 | +| `_mbsinc` | 19 | 8 | +| `realloc` | 12 | 12 | +| `??1exception@@UAE@XZ` | 9 | 9 | +| `rand` | 9 | 8 | +| `sprintf` | 7 | 7 | +| `??0exception@@QAE@XZ` | 6 | 6 | +| `_mbsstr` | 6 | 4 | +| `printf` | 6 | 2 | +| `qsort` | 6 | 6 | +| `_mbschr` | 5 | 5 | +| `ceil` | 4 | 4 | +| `wcslen` | 4 | 4 | +| `_ismbcdigit` | 3 | 2 | +| `_ismbcspace` | 3 | 3 | +| `_mbclen` | 3 | 3 | +| `_mbscspn` | 3 | 3 | +| `_mbsnbcmp` | 3 | 3 | +| `_mbspbrk` | 3 | 3 | +| `_mbsrchr` | 3 | 3 | +| `_mbsspn` | 3 | 3 | +| `_stricmp` | 3 | 3 | +| `atoi` | 3 | 2 | +| `exit` | 3 | 3 | +| `ldexp` | 3 | 3 | +| `??0exception@@QAE@ABV0@@Z` | 2 | 2 | +| `__getmainargs` | 2 | 2 | +| `__p__commode` | 2 | 2 | +| `__p__fmode` | 2 | 2 | +| `__set_app_type` | 2 | 2 | +| `__setusermatherr` | 2 | 2 | +| `_c_exit` | 2 | 1 | +| `_cexit` | 2 | 1 | +| `_errno` | 2 | 2 | +| `_exit` | 2 | 1 | +| `_ismbblead` | 2 | 1 | +| `_strlwr` | 2 | 2 | +| `_strrev` | 2 | 2 | +| `_strupr` | 2 | 2 | +| `fclose` | 2 | 2 | +| `fopen` | 2 | 2 | +| `fprintf` | 2 | 2 | +| `frexp` | 2 | 2 | +| `memchr` | 2 | 2 | +| `perror` | 2 | 2 | +| `strncmp` | 2 | 2 | +| `toupper` | 2 | 2 | +| `vsprintf` | 2 | 2 | +| `??1type_info@@UAE@XZ` | 1 | 1 | +| `??2@YAPAXI@Z` | 1 | 1 | +| `??3@YAXPAX@Z` | 1 | 1 | +| `??_U@YAPAXI@Z` | 1 | 1 | +| `??_V@YAXPAX@Z` | 1 | 1 | +| `?terminate@@YAXXZ` | 1 | 1 | +| `_CIacos` | 1 | 1 | +| `_CIasin` | 1 | 1 | +| `_CIfmod` | 1 | 1 | +| `_CIpow` | 1 | 1 | +| `_CItanh` | 1 | 1 | +| `_CxxThrowException` | 1 | 1 | +| `_XcptFilter` | 1 | 1 | +| `__CxxFrameHandler` | 1 | 1 | +| `__dllonexit` | 1 | 1 | +| `__security_error_handler` | 1 | 1 | +| `_acmdln` | 1 | 1 | +| `_adjust_fdiv` | 1 | 1 | +| `_amsg_exit` | 1 | 1 | +| `_beginthread` | 1 | 1 | +| `_chdrive` | 1 | 1 | +| `_controlfp` | 1 | 1 | +| `_except_handler3` | 1 | 1 | +| `_findclose` | 1 | 1 | +| `_getdrive` | 1 | 1 | +| `_initterm` | 1 | 1 | +| `_isnan` | 1 | 1 | +| `_itoa` | 1 | 1 | +| `_itow` | 1 | 1 | +| `_onexit` | 1 | 1 | +| `_purecall` | 1 | 1 | +| `_snprintf` | 1 | 1 | +| `_snwprintf` | 1 | 1 | +| `_splitpath` | 1 | 1 | +| `_strnicmp` | 1 | 1 | +| `_vsnprintf` | 1 | 1 | +| `_vsnwprintf` | 1 | 1 | +| `_wchdir` | 1 | 1 | +| `_wcsicmp` | 1 | 1 | +| `_wcsupr` | 1 | 1 | +| `_wfindfirst` | 1 | 1 | +| `_wfindnext` | 1 | 1 | +| `_wfopen` | 1 | 1 | +| `_wgetcwd` | 1 | 1 | +| `_wmakepath` | 1 | 1 | +| `_wmkdir` | 1 | 1 | +| `_wsplitpath` | 1 | 1 | +| `abort` | 1 | 1 | +| `atol` | 1 | 1 | +| `isalpha` | 1 | 1 | +| `isdigit` | 1 | 1 | +| `isspace` | 1 | 1 | +| `iswalpha` | 1 | 1 | +| `iswdigit` | 1 | 1 | +| `iswspace` | 1 | 1 | +| `puts` | 1 | 1 | +| `sscanf` | 1 | 1 | +| `strchr` | 1 | 1 | +| `strncat` | 1 | 1 | +| `strncpy` | 1 | 1 | +| `strstr` | 1 | 1 | +| `time` | 1 | 1 | +| `tolower` | 1 | 1 | +| `wcscat` | 1 | 1 | +| `wcschr` | 1 | 1 | +| `wcscmp` | 1 | 1 | +| `wcscpy` | 1 | 1 | +| `wcsncmp` | 1 | 1 | +| `wcsncpy` | 1 | 1 | +| `wcsrchr` | 1 | 1 | +| `wcsstr` | 1 | 1 | + +### `user32.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `ClientToScreen` | 8 | 8 | +| `GetClientRect` | 7 | 7 | +| `DestroyWindow` | 6 | 5 | +| `PeekMessageW` | 6 | 5 | +| `SendMessageA` | 6 | 6 | +| `EndDialog` | 5 | 2 | +| `LoadStringA` | 5 | 4 | +| `CallWindowProcA` | 4 | 3 | +| `GetAsyncKeyState` | 4 | 4 | +| `GetForegroundWindow` | 4 | 4 | +| `GetWindowRect` | 4 | 4 | +| `MessageBoxA` | 4 | 4 | +| `OffsetRect` | 4 | 4 | +| `SetWindowLongA` | 4 | 4 | +| `ShowWindow` | 4 | 3 | +| `wsprintfA` | 4 | 3 | +| `DefWindowProcW` | 3 | 2 | +| `DialogBoxIndirectParamA` | 3 | 3 | +| `DispatchMessageW` | 3 | 3 | +| `GetCursorPos` | 3 | 3 | +| `GetPropA` | 3 | 3 | +| `GetQueueStatus` | 3 | 3 | +| `GetSystemMetrics` | 3 | 3 | +| `GetWindowLongA` | 3 | 2 | +| `InvalidateRect` | 3 | 2 | +| `IsDlgButtonChecked` | 3 | 2 | +| `LoadCursorA` | 3 | 3 | +| `MessageBoxW` | 3 | 3 | +| `MoveWindow` | 3 | 3 | +| `MsgWaitForMultipleObjects` | 3 | 3 | +| `PostThreadMessageW` | 3 | 3 | +| `RemovePropA` | 3 | 3 | +| `SetCursorPos` | 3 | 3 | +| `ShowCursor` | 3 | 3 | +| `TranslateMessage` | 3 | 3 | +| `CharNextExA` | 2 | 2 | +| `CreateWindowExA` | 2 | 2 | +| `CreateWindowExW` | 2 | 2 | +| `DefWindowProcA` | 2 | 2 | +| `DialogBoxParamA` | 2 | 2 | +| `DispatchMessageA` | 2 | 2 | +| `EnableWindow` | 2 | 2 | +| `GetCapture` | 2 | 2 | +| `GetCaretBlinkTime` | 2 | 2 | +| `GetDesktopWindow` | 2 | 2 | +| `GetDlgItem` | 2 | 2 | +| `GetFocus` | 2 | 2 | +| `GetKeyState` | 2 | 2 | +| `GetKeyboardLayout` | 2 | 2 | +| `GetMessageA` | 2 | 2 | +| `GetParent` | 2 | 2 | +| `GetSysColor` | 2 | 2 | +| `IsWindowUnicode` | 2 | 2 | +| `LoadCursorW` | 2 | 2 | +| `LoadIconA` | 2 | 2 | +| `LoadIconW` | 2 | 2 | +| `PeekMessageA` | 2 | 2 | +| `PostMessageA` | 2 | 2 | +| `PostQuitMessage` | 2 | 2 | +| `PtInRect` | 2 | 2 | +| `RegisterClassExA` | 2 | 2 | +| `RegisterClassExW` | 2 | 2 | +| `RegisterWindowMessageW` | 2 | 2 | +| `ReleaseCapture` | 2 | 2 | +| `SendMessageW` | 2 | 2 | +| `SetCapture` | 2 | 2 | +| `SetCursor` | 2 | 2 | +| `SetDlgItemTextA` | 2 | 2 | +| `SetPropA` | 2 | 2 | +| `SetWindowTextA` | 2 | 2 | +| `UnregisterClassA` | 2 | 2 | +| `UpdateWindow` | 2 | 2 | +| `WaitMessage` | 2 | 2 | +| `wsprintfW` | 2 | 2 | +| `wvsprintfW` | 2 | 2 | + +### `wsock32.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `select` | 10 | 8 | +| `WSAGetLastError` | 8 | 6 | +| `htons` | 6 | 5 | +| `ioctlsocket` | 5 | 4 | +| `bind` | 4 | 4 | +| `closesocket` | 4 | 4 | +| `send` | 4 | 3 | +| `socket` | 4 | 4 | +| `WSAStartup` | 3 | 3 | +| `accept` | 2 | 2 | +| `connect` | 2 | 2 | +| `gethostbyname` | 2 | 2 | +| `inet_addr` | 2 | 2 | +| `inet_ntoa` | 2 | 2 | +| `listen` | 2 | 2 | +| `ntohs` | 2 | 2 | +| `recv` | 2 | 2 | +| `recvfrom` | 2 | 2 | +| `sendto` | 2 | 2 | +| `setsockopt` | 2 | 2 | +| `__WSAFDIsSet` | 1 | 1 | + +### `winmm.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `timeGetTime` | 13 | 11 | +| `timeKillEvent` | 4 | 4 | +| `mixerSetControlDetails` | 3 | 3 | +| `waveOutOpen` | 3 | 2 | +| `waveOutWrite` | 3 | 3 | +| `mixerClose` | 2 | 2 | +| `mixerGetControlDetailsA` | 2 | 2 | +| `mixerGetDevCapsA` | 2 | 2 | +| `mixerGetLineControlsA` | 2 | 2 | +| `mixerGetLineInfoA` | 2 | 2 | +| `mixerGetNumDevs` | 2 | 2 | +| `mixerOpen` | 2 | 2 | +| `timeBeginPeriod` | 2 | 2 | +| `timeEndPeriod` | 2 | 2 | +| `timeSetEvent` | 2 | 2 | +| `waveOutClose` | 2 | 2 | +| `waveOutGetDevCapsA` | 2 | 2 | +| `waveOutGetNumDevs` | 2 | 2 | +| `waveOutMessage` | 2 | 2 | +| `waveOutPrepareHeader` | 2 | 2 | +| `waveOutReset` | 2 | 2 | +| `waveOutUnprepareHeader` | 2 | 2 | + +### `ole32.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `CoUninitialize` | 15 | 13 | +| `CoCreateInstance` | 11 | 10 | +| `CoTaskMemAlloc` | 9 | 9 | +| `CoTaskMemFree` | 9 | 9 | +| `CoInitialize` | 7 | 7 | +| `CoFreeUnusedLibraries` | 3 | 3 | +| `CoInitializeEx` | 3 | 3 | + +### `advapi32.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `RegCloseKey` | 11 | 10 | +| `RegQueryValueExA` | 8 | 6 | +| `RegOpenKeyExA` | 7 | 7 | +| `RegSetValueExA` | 6 | 4 | +| `RegQueryValueExW` | 5 | 5 | +| `RegSetValueExW` | 5 | 5 | +| `RegCreateKeyExA` | 4 | 4 | +| `RegCreateKeyExW` | 2 | 2 | +| `RegDeleteValueW` | 2 | 2 | + +### `msvcp71.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@ABV01@@Z` | 7 | 7 | +| `??1?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@XZ` | 6 | 6 | +| `??0?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@PBD@Z` | 5 | 5 | + +### `imm32.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `ImmAssociateContext` | 1 | 1 | +| `ImmGetCandidateListA` | 1 | 1 | +| `ImmGetCandidateListW` | 1 | 1 | +| `ImmGetCompositionStringA` | 1 | 1 | +| `ImmGetCompositionStringW` | 1 | 1 | +| `ImmGetContext` | 1 | 1 | +| `ImmGetConversionStatus` | 1 | 1 | +| `ImmGetDefaultIMEWnd` | 1 | 1 | +| `ImmGetIMEFileNameA` | 1 | 1 | +| `ImmGetOpenStatus` | 1 | 1 | +| `ImmGetVirtualKey` | 1 | 1 | +| `ImmIsIME` | 1 | 1 | +| `ImmNotifyIME` | 1 | 1 | +| `ImmReleaseContext` | 1 | 1 | +| `ImmSetConversionStatus` | 1 | 1 | +| `ImmSetOpenStatus` | 1 | 1 | +| `ImmSimulateHotKey` | 1 | 1 | + +### `d3dx9_25.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `D3DXCreateTextureFromFileExW` | 1 | 1 | +| `D3DXCreateVolumeTexture` | 1 | 1 | +| `D3DXFilterTexture` | 1 | 1 | +| `D3DXLoadSurfaceFromFileW` | 1 | 1 | +| `D3DXLoadSurfaceFromMemory` | 1 | 1 | +| `D3DXLoadSurfaceFromSurface` | 1 | 1 | +| `D3DXLoadVolumeFromMemory` | 1 | 1 | +| `D3DXLoadVolumeFromVolume` | 1 | 1 | +| `D3DXMatrixInverse` | 1 | 1 | +| `D3DXSaveSurfaceToFileW` | 1 | 1 | +| `D3DXVec3Hermite` | 1 | 1 | + +### `gdi32.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `SetTextColor` | 3 | 2 | +| `CreateFontIndirectA` | 2 | 2 | +| `DeleteObject` | 2 | 2 | +| `GetObjectA` | 2 | 2 | +| `GetStockObject` | 2 | 2 | + +### `shell32.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `SHGetFolderPathW` | 3 | 3 | +| `ShellExecuteA` | 2 | 2 | + +### `oleaut32.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `SysAllocString` | 2 | 2 | +| `SysFreeString` | 2 | 2 | + +### `version.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `GetFileVersionInfoA` | 1 | 1 | +| `GetFileVersionInfoSizeA` | 1 | 1 | +| `VerQueryValueA` | 1 | 1 | + +### `d3d9.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `Direct3DCreate9` | 1 | 1 | + +### `dinput8.dll` + +| Import | Code refs | Functions | +|---|---:|---:| +| `DirectInput8Create` | 1 | 1 | diff --git a/docs/modern/X64_LAYOUT_DRIFT.tsv b/docs/modern/X64_LAYOUT_DRIFT.tsv new file mode 100644 index 0000000000..1ac878f6b4 --- /dev/null +++ b/docs/modern/X64_LAYOUT_DRIFT.tsv @@ -0,0 +1,1046 @@ +class pdb_size_x86 +C3DAnimFileBoneMaskChunk 0x24 +C3DAnimFileHelpersChunk 0x34 +C3DAnimFileMovementVectorChunk 0x40 +C3DAnimFileObjectChunk 0x48 +C3DAnimFileRootChunk 0x3c +C3DAnimFileSequenceChunk 0x58 +C3DAnimFileTimingEventChunk 0x18 +C3DAnimFileXAllocationSizeChunk 0x14 +C3DAnimFileXSequenceChunk 0x20 +C3DAnimation2 0x88 +C3DAnimationInfo 0x28 +C3DAnimationState 0x10 +C3DAnimationStateAlpha 0x14 +C3DAnimationTransitionState 0x14 +C3DMeshFileBoneChunk 0x78 +C3DMeshFileClothChunk 0x50 +C3DMeshFileConvexVolumeChunk 0x48 +C3DMeshFileDummyObjectChunk 0x8c +C3DMeshFileEngineMaterialChunk 0x5c +C3DMeshFileGroupChunk 0x48 +C3DMeshFileHelperPointChunk 0x4c +C3DMeshFileHelpersChunk 0x34 +C3DMeshFileMapChunk 0x44 +C3DMeshFileMaterialChunk 0x58 +C3DMeshFileMaterialExtChunk 0x14 +C3DMeshFileMaterialListChunk 0x34 +C3DMeshFilePartitionChunk 0x18 +C3DMeshFilePrimitiveChunk 0x44 +C3DMeshFileRootChunk 0x34 +C3DMeshFileSmoothingGroupsChunk 0x20 +C3DMeshFileSubMeshChunk 0x48 +C3DMeshFileTransformationChunk 0x64 +C3DMeshFileTrianglesChunk 0x44 +C3DMeshFileUniqueVerticesChunk 0x20 +C3DMeshFileVerticesChunk 0x20 +C3DMeshFileXAnimatedPrimitiveBlockChunk 0x78 +C3DMeshFileXAnimatedPrimitiveChunk 0x3c +C3DMeshFileXBoundingBoxChunk 0x28 +C3DMeshFileXBoundingSphereChunk 0x20 +C3DMeshFileXMappingInfoChunk 0x14 +C3DMeshFileXTextureIndexChunk 0x14 +C3DMeshFileXTriangleStripChunk 0x20 +C3DMeshFileXUVTangentChunk 0x30 +C3DMeshFileXVertexDataChunk 0x50 +C3DMeshInfo 0x60 +CACompressor 0x1c +CAIBrain 0xb0 +CAICreatureWillPowerIndicatorDef 0x2c +CAIGameCameraBase 0x4 +CAIScratchpadDef 0x9c +CAIStateGroupBase 0x14 +CAIStateGroupBehaviourBase 0x34 +CAIStateGroup_ApprenticeHero 0x30 +CAIStateGroup_AvoidRegionExit 0x20 +CAIStateGroup_BalverineCornered 0x34 +CAIStateGroup_BalverineJumpAtFleeingEnemy 0x20 +CAIStateGroup_BanditGossip 0x28 +CAIStateGroup_BanditKingCharge 0x1c +CAIStateGroup_BanditKingLunge 0x20 +CAIStateGroup_BanditKingThrowWeapons 0x1c +CAIStateGroup_BanditPlayDice 0x28 +CAIStateGroup_BanditPlot 0x28 +CAIStateGroup_BirdGroundWander 0x18 +CAIStateGroup_BriarRose 0x20 +CAIStateGroup_BriarRoseWillUse 0x20 +CAIStateGroup_BuyFromSeller 0x18 +CAIStateGroup_CarryCrateAndFollow 0x38 +CAIStateGroup_ChickenBeg 0x14 +CAIStateGroup_CleanHouse 0x28 +CAIStateGroup_CreatureAdvanceInCover 0x50 +CAIStateGroup_CreatureFindCover 0x50 +CAIStateGroup_CreatureRangedCombat2 0x20 +CAIStateGroup_CreatureRangedCombatFireAtSpecificTargets 0x1c +CAIStateGroup_CreatureRangedCombatUseWillShield 0x14 +CAIStateGroup_CreatureTakeCover 0x50 +CAIStateGroup_CreatureUseCoverBase 0x50 +CAIStateGroup_Drunk 0x18 +CAIStateGroup_EarthTroll 0x34 +CAIStateGroup_EatAtHome 0x4c +CAIStateGroup_EnemyCreatureBase 0x1c +CAIStateGroup_FollowPlayer 0x20 +CAIStateGroup_Gaze 0x30 +CAIStateGroup_GazeAtHome 0x30 +CAIStateGroup_GazeAtWork 0x30 +CAIStateGroup_GazeOutside 0x30 +CAIStateGroup_Guildmaster 0x40 +CAIStateGroup_HandyMan 0x18 +CAIStateGroup_HoverAttack 0x1c +CAIStateGroup_HoverAttackArea 0x28 +CAIStateGroup_HoverAttackUnlessTargetIsFleeing 0x1c +CAIStateGroup_HoverHornetQueen 0x70 +CAIStateGroup_HoverHornetQueenTakeDamage 0x28 +CAIStateGroup_HoverKeepInArea 0x28 +CAIStateGroup_HoverWander 0x1c +CAIStateGroup_HoverWanderAroundThing 0x24 +CAIStateGroup_IceTrollIcicleAssault 0x1c +CAIStateGroup_IceTrollIcicleAttack 0x1c +CAIStateGroup_IceTrollPoundGroundAttack 0x1c +CAIStateGroup_IdleAtHome 0x14 +CAIStateGroup_JackDragon 0x140 +CAIStateGroup_JackOfBladesBase 0x54 +CAIStateGroup_JackOfBladesStage2 0x5c +CAIStateGroup_KidHurtSelfRunToMother 0x18 +CAIStateGroup_KidWanderNearKids 0x14 +CAIStateGroup_LightHomeCandle 0x1c +CAIStateGroup_LightWorkCandle 0x1c +CAIStateGroup_LookAtInterestingThings 0x18 +CAIStateGroup_MazeDefensiveMeleeCombat 0x24 +CAIStateGroup_MazeRangedCombat 0x24 +CAIStateGroup_MazeShieldedChase 0x20 +CAIStateGroup_MazeTeleport 0x20 +CAIStateGroup_MazeTutorialBattle 0x24 +CAIStateGroup_MazeTutorialBattleRespondToLightning 0x24 +CAIStateGroup_MazeTutorialBattleTeleport 0x30 +CAIStateGroup_MazeTutorialBattleUseShield 0x24 +CAIStateGroup_MeleeCombatNearby 0x20 +CAIStateGroup_MeleeCombatNearby2 0x20 +CAIStateGroup_MeleeCombatNearbyAttackWhenVeryClose 0x1c +CAIStateGroup_MeleeCombatNearbyBase 0x1c +CAIStateGroup_MinionFollowThing 0x28 +CAIStateGroup_MinionGossip 0x24 +CAIStateGroup_MinionGuardGreetPlayer 0x14 +CAIStateGroup_MinionGuardReactToBribe 0x24 +CAIStateGroup_MinionGuardReactToCriminal 0x34 +CAIStateGroup_MinionHasInformation 0x24 +CAIStateGroup_MinionKidAnnoy 0x1c +CAIStateGroup_MinionKidCalledToMother 0x14 +CAIStateGroup_MinionKidGroupTag 0x30 +CAIStateGroup_MinionLeaveRegion 0x20 +CAIStateGroup_MinionListenToStory 0x14 +CAIStateGroup_MinionReactToCrime 0x24 +CAIStateGroup_MinionReactToCriminal 0x30 +CAIStateGroup_MinionReactToNoise 0x28 +CAIStateGroup_MinionReactToPlayerInteraction 0x3c +CAIStateGroup_MinionReactToTrespassing 0x20 +CAIStateGroup_MinionSitAtInn 0x3c +CAIStateGroup_MinionSitOnSeat 0x40 +CAIStateGroup_MinionSitOnSeatBase 0x3c +CAIStateGroup_MinionSitOutside 0x44 +CAIStateGroup_MinionSleep 0x64 +CAIStateGroup_MinionWander 0x14 +CAIStateGroup_MinionWatchFires 0x1c +CAIStateGroup_MoveGoodsBase 0x1c +CAIStateGroup_OpinionReactionRoot 0x98 +CAIStateGroup_PerformAction 0x18 +CAIStateGroup_PrepareMeal 0x44 +CAIStateGroup_RangedCombatUsePreferredFirePoint 0x28 +CAIStateGroup_ReactToDeadLeader 0x20 +CAIStateGroup_RecoverStealableItem 0x40 +CAIStateGroup_RockTroll 0x34 +CAIStateGroup_ScorpionKing 0x44 +CAIStateGroup_SeekEnemy 0x1c +CAIStateGroup_SeekPerceivedThing 0x18 +CAIStateGroup_SeekScriptTarget 0x1c +CAIStateGroup_ServeFoodAtHome 0x4c +CAIStateGroup_ServeFoodAtTavern 0x4c +CAIStateGroup_SitInPub 0x3c +CAIStateGroup_SitRoundCampfire 0x28 +CAIStateGroup_StandStill 0x14 +CAIStateGroup_StealStealableItem 0x3c +CAIStateGroup_StealthSeekEnemy 0x28 +CAIStateGroup_SummonerCharge 0x24 +CAIStateGroup_SummonerLightningAttack 0x24 +CAIStateGroup_SummonerSummon 0x1c +CAIStateGroup_TavernKeeper 0x68 +CAIStateGroup_TavernServer 0x9c +CAIStateGroup_TestTestTest 0x14 +CAIStateGroup_ThrowLogsOnFire 0x1c +CAIStateGroup_Thunder 0x2c +CAIStateGroup_TrollBase 0x34 +CAIStateGroup_VersusEnemyBase 0x1c +CAIStateGroup_VersusScorpionKing 0x28 +CAIStateGroup_VillageTaskPickCrops 0x1c +CAIStateGroup_WanderAroundShops 0x28 +CAIStateGroup_WhisperPotionCombat 0x3c +CAIStateGroup_WhisperRetreat 0x30 +CAIStateGroup_WifeGiveYouGift 0x20 +CAIStateGroup_WorkAtSchool 0x14 +CAIStateGroup_WorkHammerAnvil 0x4c +CAIStateGroup_WorkSawingWood 0x48 +CAInputProcess 0x14 +CANavQuadTreeNode 0x10 +CAProgressDisplay 0x4 +CASuspendableProcess 0x10 +CActionDoCreatureAction 0x1c +CActionUseDef 0x34 +CActivateQuestDef 0x30 +CActiveFile 0xc8 +CAnimComponentCombatComboChain 0x10 +CAnimComponentCombatFlourishTargetOffset 0x11 +CAnimComponentCombatHandedness 0x10 +CAnimComponentCombatMisc 0xa +CAnimComponentCombatRecoil 0xc +CAnimComponentDelayTime 0xc +CAnimComponentFlags 0xc +CAnimComponentFollowingOnAnim 0xc +CAnimComponentFollowingOnAnimFilter 0xc +CAnimComponentSetCreatureMode 0xc +CAnimComponentStrikeResponseAnim 0xc +CAnimComponentTransitionInTime 0xc +CAnimFilterAnyGroup 0x4 +CAnimFilterNoGroup 0x4 +CApprenticeSpeedTest 0x20 +CAugmentationDef 0x4c +CBankFile 0x110 +CBankFileAsync 0x164 +CBankFileAsyncData 0x48 +CBankStateBlock 0x4 +CBaseObject 0x8 +CBettingDef 0x58 +CBonusItemDef 0x74 +CBriarRoseDef 0x54 +CBrushLibraryDialog 0x12c +CBufferedFileDataOutputStream 0x24 +CBulletTimeManager 0x1c +CBusyResourceManager 0x14 +CBuyHouseDef 0x26 +CCPPointerInfo 0xc +CCameraCollisionDef 0x2c +CCarriedReadableDef 0x30 +CCarryableDef 0x50 +CCategory 0x30 +CCharString 0x4 +CCharStringData 0x11 +CChecksumCacheFile 0x18 +CChestDef 0x3c +CChunkedFileChunk 0x10 +CChunkedFileRawDataChunk 0x18 +CChunkedFileSuperChunk 0x34 +CClass 0x11 +CClassFactory 0x28 +CCodeSectionManager 0x2c +CCombatAbilityAttackBase 0x48 +CCombatAbilityBlockCounterAttackDef 0x48 +CCombatAbilityBlockingData 0x34 +CCombatAbilityData 0x18 +CCombatAbilityFlourishCounterAttackDef 0x48 +CCombatAbilityMeleeAttackData 0x20 +CCombatAbilityProjectileAttackData 0x20 +CCombatAbilityStrafeDef 0x44 +CCombatAbilityStrafingData 0x1c +CCombatAction_ControlledStrafeJump 0x148 +CCombatAction_GenericResponseToDecapitation 0x12c +CCombatAction_GenericResponseToExplosion 0x150 +CCombatAction_GenericStrikeResponseKnockdown 0x154 +CCombatAction_GenericStrikeResponseKnockdownRecoverBase 0x130 +CCombatAction_GenericStrikeResponseKnockdownRecoverFromDeath 0x130 +CCombatAction_KickThingOnGround 0x148 +CCombatAction_StabThing 0x138 +CCombatAction_StrikeResponseJackOfBladesIntoRush 0x164 +CCombatAction_StrikeResponseScorpionKing 0x160 +CCombatAction_WillEnflameInto 0x130 +CCombatAction_WillShieldInto 0x134 +CCombatSequence2StrikeCombo 0x2c +CCombatSequenceGeneric 0x30 +CCombatSequenceJacksCombo 0x2c +CCombatSequenceNonInterruptable2StrikeCombo 0x2c +CCombatSequenceUnblockableFacing 0x2c +CCombatSequenceWhisperAdvance 0x2c +CCombatSequenceWhisperAttack360 0x2c +CCombatSequenceWhisperAttackBigCombo 0x2c +CCombatSequenceWhisperAttackCombo 0x2c +CCombatSequenceWhisperBoast 0x2c +CCombatSequenceWhisperIdles 0x2c +CCombatSequenceWhisperLunge 0x2c +CCombatSequenceWhisperSortYourselfOut 0x2c +CCombatSequenceWhisperStrafeAround 0x2c +CCombatSequenceWhisperStrikeStrafeBack 0x2c +CCombatSequenceWhisperTooCloseLeapBack 0x2c +CCombatSequence_BanditAdvanceFar 0x2c +CCombatSequence_BanditAttackFar 0x2c +CCombatSequence_BanditAttackMiddle 0x2c +CCombatSequence_BanditAttackNear 0x2c +CCombatSequence_BanditAttackSide 0x2c +CCombatSequence_BanditBoast 0x2c +CCombatSequence_BanditIdleMiddle 0x2c +CCombatSequence_BanditIdleNear 0x2c +CCombatSequence_BanditKingAdvance 0x2c +CCombatSequence_BanditKingAttack 0x2c +CCombatSequence_BanditKingBackOff 0x2c +CCombatSequence_BanditKingSpinAround 0x2c +CCombatSequence_BanditUnblockableAttack 0x2c +CCombatSequence_BattleCharge 0x30 +CCombatSequence_FodderCombatIdle 0x2c +CCombatSequence_FodderMainAttack 0x2c +CCombatSequence_FodderSecondaryAttack 0x2c +CCombatSequence_FodderUnblockableMainAttack 0x2c +CCombatSequence_GenericLeader 0x2c +CCombatSequence_GenericRangedAttack 0x2c +CCombatSequence_KingScorpionTailPlunge 0x2c +CCombatSequence_NonInterruptableBoxStrafe 0x2c +CCombatSequence_ScorpionLungeAttack 0x30 +CCombatSequence_TrollBurrowPursue 0x2c +CCombatSequence_TrollBurrowPursueNoZone 0x2c +CCombatSequence_TrollDoubleSwipe 0x2c +CCombatSequence_TrollDoubleSwipeNonMelee 0x2c +CCombatSequence_TrollOpeningGroundPound 0x30 +CCombatSequence_TrollProjectileAttack 0x2c +CCombatSequence_TrollRockAdvance 0x2c +CCombatSequence_TrollRockIdle 0x2c +CCombatSequence_TrollRockPursue 0x2c +CCombatSequence_TrollRockPursueNoZone 0x2c +CCombatSequence_TrollRockShuffleBack 0x2c +CCombatSequence_TrollRockThrowRock 0x2c +CCombatSequence_VillagerAdvance 0x2c +CCombatSequence_VillagerAttack 0x2c +CCombatSequence_VillagerBackOff 0x2c +CCombatSequence_VillagerCombo 0x2c +CCombatSequence_VillagerComboUnblockable 0x2c +CCombatSequence_VillagerNearAttack 0x2c +CCombatSequence_VillagerStepIn 0x2c +CCombatSequence_WillEnflame 0x2c +CCombatSequence_WillForcePush 0x2c +CCombatSequence_WillForcePushLongRange 0x2c +CCombatSequence_WillLightning 0x2c +CCombatSequence_WillPhysicalShield 0x2c +CCombatSequence_WillPhysicalShieldNonMelee 0x2c +CCombatTypeDef 0xc0 +CCompressedMemoryDataInputStream 0x24 +CCompressorZlib 0x58 +CConsole 0x9c +CConsoleClasslessCommand 0x18 +CConsoleCommandBase 0x14 +CConsoleCommandLine 0x10 +CConsoleCommandParameters 0xc +CConsoleInputBase 0x8 +CConsoleVariable 0x10 +CContainmentVolume 0x10 +CContextSensitiveItemDef 0x3c +CCrateStackDef 0x30 +CCreatureActionBase 0xb4 +CCreatureActionHero180RunningTurn 0x138 +CCreatureActionHeroRunningStop 0x12c +CCreatureAction_ActivateQuest 0xc4 +CCreatureAction_AddRealObjectToInventory 0xbc +CCreatureAction_AimMissileWeapon 0xbc +CCreatureAction_Ambush 0x138 +CCreatureAction_BalverineBreakOffFromCombat 0x12c +CCreatureAction_BalverineBreakOffFromCombatLong 0x12c +CCreatureAction_BalverineCorneredJumpIntoSky 0x130 +CCreatureAction_BalverineDropInFromSky 0x12c +CCreatureAction_BalverineLungeAttack 0x12c +CCreatureAction_BanditKingAdvance 0x12c +CCreatureAction_BanditKingAttack 0x130 +CCreatureAction_BanditKingBackOff 0x12c +CCreatureAction_BanditKingGetHit 0x160 +CCreatureAction_BanditKingLunge 0x12c +CCreatureAction_BanditKingStuck 0x140 +CCreatureAction_BanditKingThrowWeapons 0xbc +CCreatureAction_BanditKingUnstuck 0x130 +CCreatureAction_BattleChargeImpact 0x13c +CCreatureAction_BattleChargeMiss 0x13c +CCreatureAction_BattleChargeTurnLeft 0x12c +CCreatureAction_BattleChargeTurnRight 0x12c +CCreatureAction_BeDazed 0xb4 +CCreatureAction_BeingForcePushed 0x12c +CCreatureAction_BlockBase 0x13c +CCreatureAction_BlockCounter 0x12c +CCreatureAction_BlockRespond 0x14c +CCreatureAction_BlowHorn 0xbc +CCreatureAction_BodgeSideStrike 0x12c +CCreatureAction_BriarRoseConcentrateInto 0x138 +CCreatureAction_BriarRoseForcePush 0x134 +CCreatureAction_BriarRoseIntoInvulnerable 0x134 +CCreatureAction_BriarRoseInvulnerableTeleport 0x134 +CCreatureAction_BriarRoseShotFarInto 0x12c +CCreatureAction_BriarRoseShotFarOutof 0x134 +CCreatureAction_BuildDrainLifeSpell 0x12c +CCreatureAction_BuildFireballSpell 0x12c +CCreatureAction_BuildGhostSwordSpell 0x12c +CCreatureAction_BuildHealLifeSpell 0x12c +CCreatureAction_BuildUpBattleCharge 0x12c +CCreatureAction_BuildUpDivineWrathSpell 0x12c +CCreatureAction_BuildUpPhysicalShieldSpell 0x12c +CCreatureAction_BuildUpTimeSpell 0x12c +CCreatureAction_BuildUpUnholyPowerSpell 0x12c +CCreatureAction_BuildUpWillEnflameSpell 0x12c +CCreatureAction_BuildUpWillLightningSpell 0x12c +CCreatureAction_BuildUpWillSummonSpell 0x12c +CCreatureAction_BuildWillForceSpell 0x12c +CCreatureAction_BuildWillTurncoatSpell 0x12c +CCreatureAction_CastAssassinRush 0x150 +CCreatureAction_CastBattleCharge 0x148 +CCreatureAction_CastDivineWrathSpell 0x12c +CCreatureAction_CastPhysicalShieldSpell 0x13c +CCreatureAction_CastSkillMultiArrow 0x130 +CCreatureAction_CastStrengthBerserk 0x12c +CCreatureAction_CastStrengthMultiStrike 0x130 +CCreatureAction_CastStrengthMultiStrikeProper 0x130 +CCreatureAction_CastTimeSpell 0x130 +CCreatureAction_CastUnholyPowerSpell 0x12c +CCreatureAction_CastWillEnflameSpell 0x134 +CCreatureAction_CastWillFireballSpell 0x134 +CCreatureAction_CastWillForceSpell 0x134 +CCreatureAction_CastWillGhostSwordSpell 0x130 +CCreatureAction_CastWillLightningSpell 0x134 +CCreatureAction_CastWillReclaimUndeadFollower 0x134 +CCreatureAction_CastWillSummonSpell 0x130 +CCreatureAction_Charge 0x12c +CCreatureAction_CombatBoast 0x12c +CCreatureAction_CombatIdle 0x12c +CCreatureAction_Curiosity 0xb4 +CCreatureAction_DeclarePlayerACriminal 0xbc +CCreatureAction_DiagonalStrafeBL 0x12c +CCreatureAction_DiagonalStrafeBR 0x12c +CCreatureAction_Die 0xb8 +CCreatureAction_DowseFire 0xc8 +CCreatureAction_DragonBurninateInto 0x168 +CCreatureAction_DragonFireBlastBase 0x13c +CCreatureAction_DragonFlight 0x134 +CCreatureAction_DragonHoverOutOf 0x12c +CCreatureAction_DragonLand 0x12c +CCreatureAction_DragonRearUpFull 0x134 +CCreatureAction_DragonRearUpInto 0x130 +CCreatureAction_DragonRearUpOutOf 0x12c +CCreatureAction_DragonRetreat 0x12c +CCreatureAction_DragonStrafeOutOf 0x12c +CCreatureAction_DragonSwoop 0x150 +CCreatureAction_DragonTakeOffIntoFlying 0x16c +CCreatureAction_Drink 0xc4 +CCreatureAction_DropGenericBox 0xb4 +CCreatureAction_DuckAndCover 0x138 +CCreatureAction_FillBucket 0xbc +CCreatureAction_FinishDrainLifeSpell 0x12c +CCreatureAction_FinishFireballSpell 0x12c +CCreatureAction_FinishGhostSwordSpell 0x12c +CCreatureAction_FinishHealLifeSpell 0x12c +CCreatureAction_FinishWillForceSpell 0x12c +CCreatureAction_FinishWillTurncoatSpell 0x12c +CCreatureAction_FireMissileTest 0xc0 +CCreatureAction_FireMissileWeapon 0xd4 +CCreatureAction_FodderIdle 0x12c +CCreatureAction_FodderPrimaryAttack 0x12c +CCreatureAction_FodderSecondaryAttack 0x12c +CCreatureAction_FodderUnblockablePrimaryAttack 0x12c +CCreatureAction_ForcePushOutOf 0x130 +CCreatureAction_GeneralInterruptedAttack 0x12c +CCreatureAction_GenericAimProjectileWeapon 0x12c +CCreatureAction_GenericFireProjectileWeapon 0x12c +CCreatureAction_GenericLeaderCommand 0x12c +CCreatureAction_GenericLoadProjectileWeapon 0x12c +CCreatureAction_GenericStrafeLeft 0x12c +CCreatureAction_GenericStrafeRight 0x12c +CCreatureAction_GetInBed 0xcc +CCreatureAction_GetOutOfBed 0xbc +CCreatureAction_GiveMoneyToThing 0xc0 +CCreatureAction_GoUnderground 0x12c +CCreatureAction_Hammer 0xbc +CCreatureAction_HeroLoadRangedWeapon 0xc4 +CCreatureAction_HobbeLungeAttack 0x12c +CCreatureAction_HobbeSpellcasterAim 0x134 +CCreatureAction_IceTrollIcicleAssaultInto 0x12c +CCreatureAction_IceTrollIcicleAttack 0x12c +CCreatureAction_InterruptableFarAttack 0x12c +CCreatureAction_InterruptableMidAttack 0x130 +CCreatureAction_InterruptableMidAttackAutoTurn 0x144 +CCreatureAction_JackDeathInto 0x12c +CCreatureAction_JackFall 0x12c +CCreatureAction_JackFlash 0x12c +CCreatureAction_JackFloatUp 0x13c +CCreatureAction_JackOfBladesUnblockableMidAttackNoBlend 0x12c +CCreatureAction_JackScream 0x134 +CCreatureAction_JackShotFar 0x12c +CCreatureAction_JackShotFloat 0x12c +CCreatureAction_JackShotNear 0x12c +CCreatureAction_JumpOutFromCover 0x138 +CCreatureAction_KickCreature 0xbc +CCreatureAction_LeapBack 0x12c +CCreatureAction_LeapLong 0x130 +CCreatureAction_LearnExpression 0xc4 +CCreatureAction_MagicBlast 0xbc +CCreatureAction_MidAttackFacing 0x12c +CCreatureAction_NymphGetHit 0x16c +CCreatureAction_NymphGetHitDie 0x164 +CCreatureAction_NymphIdleAfterShot 0x12c +CCreatureAction_NymphShot 0x12c +CCreatureAction_NymphSummonInto 0x12c +CCreatureAction_NymphSummonOutOf 0x12c +CCreatureAction_NymphTeleportLimboBase 0x12c +CCreatureAction_NymphTeleportOutOfLimbo 0x138 +CCreatureAction_OnGenerate 0x12c +CCreatureAction_OpenChest 0xbc +CCreatureAction_PerformExpression 0xd4 +CCreatureAction_PerformExpressionExtended 0xd4 +CCreatureAction_PickCrop 0xbc +CCreatureAction_PickUpGenericBox 0xbc +CCreatureAction_PickUpGoods 0xbc +CCreatureAction_PickUpJugToFill 0xbc +CCreatureAction_PickUpThingAtTable 0xc0 +CCreatureAction_PickupGameObject 0xbc +CCreatureAction_PlayAnimation 0xc0 +CCreatureAction_PlayAnimationFromIndex 0xb4 +CCreatureAction_PlayAnimationWithLookTurning 0xc0 +CCreatureAction_PlayCombatAnimation 0x130 +CCreatureAction_PlayCombatAnimationWithLooking 0x130 +CCreatureAction_PlayConversationAnimation 0xbc +CCreatureAction_PlayHopscotch 0xb4 +CCreatureAction_PlayerInteractionBribe 0xbc +CCreatureAction_PlayerInteractionFlirt 0xbc +CCreatureAction_PlayerInteractionGreet 0xbc +CCreatureAction_PlayerInteractionInsult 0xbc +CCreatureAction_PlayerInteractionThreaten 0xbc +CCreatureAction_PlayerInteractionViewQuests 0xbc +CCreatureAction_ProjectileCrouch 0x13c +CCreatureAction_PutDownJugFromFilling 0xbc +CCreatureAction_PutGoodsDown 0xb4 +CCreatureAction_QueenHornetShootBabies 0x12c +CCreatureAction_ReleaseAndLoopDivineWrathSpell 0x12c +CCreatureAction_ReleaseAndLoopUnholyPowerSpell 0x12c +CCreatureAction_ReleaseDivineWrathSpell 0x12c +CCreatureAction_ReleasePhysicalShieldSpell 0x12c +CCreatureAction_ReleaseStrengthBerserk 0x12c +CCreatureAction_ReleaseTimeSpell 0x130 +CCreatureAction_ReleaseUnholyPowerSpell 0x12c +CCreatureAction_ReleaseWillEnflameSpell 0x12c +CCreatureAction_ReleaseWillLightningSpell 0x12c +CCreatureAction_ReleaseWillSummonSpell 0x12c +CCreatureAction_ReloadMissileWeapon 0xbc +CCreatureAction_RemovePhysicalShieldSpell 0x12c +CCreatureAction_ScorpionCharge 0x13c +CCreatureAction_ScorpionChargeInto 0x138 +CCreatureAction_ScorpionChargeStrike 0x13c +CCreatureAction_ScorpionKingDeath 0x12c +CCreatureAction_ScorpionPrepareTailPlunge 0x138 +CCreatureAction_ScorpionRecover 0x12c +CCreatureAction_ScorpionSummon 0x12c +CCreatureAction_ScorpionTailPlunge 0x138 +CCreatureAction_ScorpionTailWithdraw 0x138 +CCreatureAction_ScorpionUnblockableLeftTurnAttack 0x12c +CCreatureAction_ScorpionUnblockableRightTurnAttack 0x12c +CCreatureAction_ScreamerAdvance 0x12c +CCreatureAction_ScreamerBackOff 0x12c +CCreatureAction_ScreamerDie 0x134 +CCreatureAction_ScreamerDrainAttack 0x134 +CCreatureAction_ScreamerDrainOutOf 0x12c +CCreatureAction_ScreamerIdle 0x12c +CCreatureAction_SendMessage 0xd0 +CCreatureAction_SheatheItemToInventory 0xbc +CCreatureAction_SheatheItemToInventory_ForDivineWrath 0xbc +CCreatureAction_SheatheItemToInventory_ForEnflame 0xbc +CCreatureAction_SheatheItemToInventory_ForUnholyPower 0xbc +CCreatureAction_ShopkeeperGetOut 0xe0 +CCreatureAction_ShopkeeperPutAway 0xbc +CCreatureAction_ShowFear 0xb4 +CCreatureAction_ShowItemToThing 0xc4 +CCreatureAction_ShowLove 0xbc +CCreatureAction_ShowTrophy 0xcc +CCreatureAction_SitDown 0xbc +CCreatureAction_SnapDragon 0x12c +CCreatureAction_SnapDragonFast 0x12c +CCreatureAction_SnapDragonOutOf 0x12c +CCreatureAction_SpiritAttack 0xc4 +CCreatureAction_SpiritHitResponse 0xc0 +CCreatureAction_SpiritReturnToHero 0xb4 +CCreatureAction_StabbedToDeath 0xb4 +CCreatureAction_StartBlocking 0x12c +CCreatureAction_StartSneaking 0xb4 +CCreatureAction_StopSneaking 0xb4 +CCreatureAction_StrafeBackward 0x12c +CCreatureAction_StrafeForward 0x12c +CCreatureAction_SummonerDead 0x12c +CCreatureAction_SummonerDie 0x12c +CCreatureAction_SummonerFlameSlice 0x134 +CCreatureAction_SummonerLightningOrbInto 0x134 +CCreatureAction_SummonerStrike 0x12c +CCreatureAction_SummonerUnsheatheStrike 0x130 +CCreatureAction_Talk 0xbc +CCreatureAction_TavernGameMasterInteraction 0xbc +CCreatureAction_TeleportIn 0x130 +CCreatureAction_TeleportOut 0x13c +CCreatureAction_TentacleRise 0x12c +CCreatureAction_TentacleStrikeGetHit 0x12c +CCreatureAction_TentacleStrikeInto 0x130 +CCreatureAction_ThrowGameObject 0xc0 +CCreatureAction_ThrowShotObject 0x12c +CCreatureAction_ThunderLightningStorm 0x12c +CCreatureAction_TrollAngryGroundPound 0x12c +CCreatureAction_TrollGetProjectile 0x12c +CCreatureAction_TrollProjectileAttack 0x134 +CCreatureAction_TrollPursue 0x12c +CCreatureAction_TrollSwipe 0x12c +CCreatureAction_TrollThrowProjectile 0x148 +CCreatureAction_TrollTunnel 0x12c +CCreatureAction_TrollWhackGroundBase 0x12c +CCreatureAction_UnblockableMidAttack 0x12c +CCreatureAction_UndeadReturnToEarth 0x12c +CCreatureAction_UndeadRiseUp 0x134 +CCreatureAction_UnsheatheItemFromInventory 0xc4 +CCreatureAction_UnsheatheItemFromInventoryWithHasteAction 0xc8 +CCreatureAction_UnsheatheToShowToSheatheTrophy 0xd0 +CCreatureAction_UnsheatheWeapons 0x130 +CCreatureAction_UseLamp 0xbc +CCreatureAction_UseObject 0xec +CCreatureAction_UseSwitch 0xbc +CCreatureAction_UseWallMount 0xbc +CCreatureAction_VillagerAdvance 0x12c +CCreatureAction_VillagerAttack 0x12c +CCreatureAction_VillagerAttackKnockdown 0x130 +CCreatureAction_VillagerBackOff 0x12c +CCreatureAction_WalkInto 0x12c +CCreatureAction_WaspQueenDeath 0x134 +CCreatureAction_WaspQueenDiveAttack 0x12c +CCreatureAction_WaspQueenLashOutAttack 0x12c +CCreatureAction_WaspQueenPrimaryAttack 0x12c +CCreatureAction_WhisperJumpIntoSky 0x12c +CCreatureAction_YellOverHere 0xb4 +CCreatureNavigationDef 0x38 +CCreatureStatsDef 0x48 +CCriticalSectionLock 0x8 +CDataInputStream 0x18 +CDataOutputStream 0x8 +CDebugManager 0x278 +CDecal 0x7c +CDecapitationDef 0x30 +CDefClassBase 0x25 +CDefaultEngineMemoryInit 0x28 +CDefinitionManager 0xf0 +CDiskFileWin32 0x1c +CDisplayEngine 0x100 +CDisplayViewManager 0x8 +CDoorDef 0x3c +CDragonActionSwoopDef 0x64 +CDrawMapInfo 0x20 +CDrawMiniMap 0x184 +CDriveManager 0x8 +CEditControlCentre 0xb74 +CEditWorldMap 0x14c +CEnemyDef 0x2c +CEngine 0x188 +CEngineBillboardGenerator 0x20 +CEngineCamera 0x228 +CEngineComponent 0xc +CEngineEnvironment 0x654 +CEngineInternalPrimitive2DAnimatedMesh 0x154 +CEngineInternalPrimitive2DClockSprite 0x94 +CEngineInternalPrimitive2DSprite 0x8c +CEngineInternalPrimitive2DStaticMesh 0x130 +CEngineInternalPrimitive2DViewport 0xa4 +CEngineInternalPrimitiveAnimatedMeshFromFile 0x12c +CEngineInternalPrimitiveBase 0x48 +CEngineInternalPrimitiveGroup 0x64 +CEngineInternalPrimitiveLight 0x70 +CEngineInternalPrimitiveOcclusionBase 0x50 +CEngineInternalPrimitiveOcclusionGroup 0x64 +CEngineInternalPrimitiveParticleGroup 0xc0 +CEngineInternalPrimitiveParticleMesh 0x88 +CEngineInternalPrimitiveParticleMeshGroup 0xfc +CEngineInternalPrimitiveParticleSpriteTrailGroup 0xd0 +CEngineInternalPrimitiveRepeatedStaticMesh 0xb0 +CEngineInternalPrimitiveRepeatedZSprite 0x94 +CEngineInternalPrimitiveShadowedSpotLight 0x7c +CEngineInternalPrimitiveStaticMesh 0xac +CEngineInternalPrimitiveStaticMeshFromFile 0xac +CEngineLandscapePatch 0x48 +CEngineLayerRenderer 0x1c +CEngineLightingManager 0x46d0 +CEngineOcclusionManager 0x240d4 +CEnginePersistentPrimitiveBase 0xc +CEnginePrimitive2DSprite 0x54 +CEnginePrimitive2DViewportManager 0x38 +CEnginePrimitiveAttachmentManager 0x1c +CEnginePrimitiveHandle 0x8 +CEnginePrimitiveManager 0xc +CEnginePrimitiveManager2DParticleGroup 0x28 +CEnginePrimitiveManager2DSprites 0x38 +CEnginePrimitiveManagerFlareSprites 0x28 +CEnginePrimitiveManagerMeshBase 0x20 +CEnginePrimitiveManagerOcclusionBase 0x20 +CEnginePrimitiveManagerParticleDecalGroup 0x124 +CEnginePrimitiveManagerParticleLineGroup 0x264 +CEnginePrimitiveManagerParticleMeshGroup 0x2e0 +CEnginePrimitiveManagerParticleSpriteGroup 0x334 +CEnginePrimitiveManagerParticleSpriteTrailGroup 0x44 +CEnginePrimitiveManagerRepeatedStaticMeshes 0x94 +CEnginePrimitiveManagerRepeatedZSprites 0xc8 +CEnginePrimitiveManagerSprites 0xc +CEnginePrimitiveManagerStaticMeshesBase 0x318 +CEnginePrimitiveOcclusionGroupManager 0x20 +CEnginePrimitiveOcclusionManager 0x38 +CEnginePrimitiveRenderer 0x350 +CEnginePrimitiveRenderer2D 0x374 +CEnginePrimitiveRendererSharedData 0x268 +CEnginePrimitiveTypeInfo 0xcc +CEnginePrimitiveTypeList 0xc +CEngineSceneEnumeratorResetVisibilityRatio 0x8 +CEngineSceneGrid 0x20 +CEngineSceneGridCell 0x48 +CEngineSceneMap 0x28 +CEngineScreenEffectColourFilter 0xec +CEngineScreenEffectDisplacementRenderer 0x1ac +CEngineScreenEffectGlowRenderer 0x280 +CEngineScreenEffectOutlineGlow 0x1e4 +CEngineScreenEffectRadialBlurRenderer 0x22c +CEngineShadowRenderer 0x340 +CEngineSimulationManager 0x1090 +CEngineSkyRenderer 0x194 +CEngineStateBlocks 0x34 +CEngineStaticMapFileBlockManager 0x14 +CEngineSubPrimitiveRenderer 0x190 +CEngineTexturePalette 0x18 +CEngineVSConstantLayoutBasic 0x150 +CEngineVSConstantLayoutLights 0x150 +CEngineVSConstantLayoutLightsBones 0x150 +CEngineVSConstantLayoutParticles 0x150 +CEngineVSConstantLayoutRepeatedMesh 0x180 +CEngineWaterBackgroundSubPatch 0x60 +CEngineWeatherRenderer 0x1328 +CEngineWeatherSettings 0x558 +CEnvironment 0x1aa0 +CEnvironmentThemeDaySetDef 0x74 +CExplodingObjectDef 0x34 +CExplosiveTrailDef 0x4c +CExpressionSubDef 0x2c +CFileDataInputStream 0x24 +CFileDataOutputStream 0xc +CFireballSpellLevelDef 0x2c +CFireheartMinigameDef 0x3c +CFishDef 0x58 +CFontBank 0x10 +CFontBankStateBlock 0x10 +CFontDataBank 0x130 +CFontDataBankEntry 0x18 +CFrontEndList 0x240 +CFrontEndManager 0xf8 +CFrontEndScreen 0x19c +CGameCameraBase 0xd8 +CGameCameraManager 0x184 +CGameJoystickManager 0xc +CGamePlayerInterface 0x8a8 +CGameScriptInterface 0x54 +CGameScriptThing 0x48 +CGameTimeManager 0x40 +CGenericVarRef 0x8 +CGoldDef 0x2c +CGraphicBankStateBlock 0x64 +CGraphicFrame 0x4 +CGraphicFrameData 0x1b +CGraphicInfo 0x2c +CGraphicInfoFrame 0x10 +CGroundNavigatorExternalInterface 0xc +CGuardDef 0x50 +CGuiVarTransferFloat 0x20 +CHairCardDef 0x2c +CHandleBasedMemoryPool 0x464 +CHasNameDef 0x34 +CHeroCentreDef 0x25 +CHeroDef 0x30 +CHeroMarriageDef 0xc8 +CHeroSpecialMovementDef 0x38 +CHeroTitleDef 0x50 +CHighlightItemDef 0x48 +CHornetDrone 0x20 +CHoverable 0x19c +CIDrawMap 0x4 +CIEngine 0xc +CIVCountedPointeeBase 0x8 +CIndexBuffer 0x14 +CInitBaseClass 0x8 +CInputProcessBetting 0x28 +CInputProcessBoastUI 0x28 +CInputProcessClickPastText 0x28 +CInputProcessConsole 0x28 +CInputProcessControlCreatureRightStick 0x2c +CInputProcessControlFreeCamera 0x2c +CInputProcessControlSpirit 0x30 +CInputProcessCreditsUI 0x28 +CInputProcessCutScene 0x28 +CInputProcessCycleSpecialCameraModes 0x28 +CInputProcessDead 0x28 +CInputProcessFireheartMinigame 0x28 +CInputProcessFreezeControls 0x28 +CInputProcessGameBase 0x28 +CInputProcessHeroAbilitiesScreen 0x3c +CInputProcessHeroInformationScreens 0x28 +CInputProcessInGameMenu 0x38 +CInputProcessInventory 0x38 +CInputProcessInventoryBase 0x34 +CInputProcessInventoryClothing 0x38 +CInputProcessInventoryExperienceScreen 0x38 +CInputProcessInventoryMagicScreen 0x38 +CInputProcessInventoryMapScreen 0x38 +CInputProcessInventoryQuestsScreen 0x40 +CInputProcessInventoryStatsScreen 0x38 +CInputProcessInventoryTradeScreen 0x38 +CInputProcessInventoryWeapons 0x38 +CInputProcessJumpingAndRolling 0x64 +CInputProcessLightning 0x28 +CInputProcessMain 0x28 +CInputProcessParalysed 0x2c +CInputProcessQuestCompletionUI 0x28 +CInputProcessQuickAccessMenu 0x28 +CInputProcessTargetLockRightStickTargetSelect 0x2c +CInputProcessTavernGame 0x28 +CInputProcessUseRangedWeaponZLock 0x28 +CInputProcessWatchForRangedWeaponThirdPersonModeTermination 0x2c +CInputProcessWatchForWillChargeUpThirdPersonModeTermination 0x2c +CInputProcessYesNoQuestion 0x2c +CInputTypeKeyboardKeyEvent 0x10 +CInputTypeMouseButtonEvent 0x10 +CInputTypeMouseMovementEvent 0x4 +CInputTypeMouseWheelMovementDownEvent 0x4 +CInputTypeMouseWheelMovementEvent 0x4 +CInputTypeMouseWheelMovementUpEvent 0x4 +CInputTypeXboxPadButtonEvent 0x8 +CInputTypeXboxPadLeftStickEvent 0x4 +CInputTypeXboxPadRightStickEvent 0x4 +CInterestingToVillagersDef 0x2c +CInventoryItemDef 0x70 +CItem 0x14 +CJackDragonDef 0x78 +CJoystick 0xd18 +CJoystickDX 0xd4c +CKeyboard 0x3414 +CKickableDef 0x54 +CKrakenTentacleDef 0x60 +CLandscapeBackgroundPatch 0x11c +CLandscapeBackgroundSubNodeBase 0x4 +CLandscapeBackgroundSubNodeSplitBranch 0x1e4 +CLandscapeBackgroundTreeNode 0xe8 +CLandscapePatchTesselator 0x28 +CLevelLoader 0x2c +CLightDef 0x5c +CLipSyncDataBank 0x200 +CList 0x240 +CLoadingMemoryFailureHandler 0x28 +CLocalDetailObjectCollectionType 0x54 +CLookDef 0x58 +CMaintainedEvent 0x38 +CManagedTexture 0x8 +CManagedTextureData 0x4c +CManagedTextureOwner 0x1c +CManager 0x100 +CMemoryAllocatorBase 0xc1c +CMemoryAllocatorFixedSize 0xc4c +CMemoryDataInputStream 0x1c +CMemoryDataOutputStream 0x1c +CMemoryFile 0x28 +CMeshBankManager 0x14 +CMeshDataBankEntry 0xc +CMiniMapDisplay 0xdc +CMirroredTextureHighlightsChunk 0x20 +CModeDragonBase 0x30 +CModeStandBase 0x68 +CMorphingSprite 0x1d8 +CMouse 0x3414 +CMovableResource 0x4 +CMovableResourceMemoryPool 0x464 +CMovie 0x1c8 +CMultiStaticMeshEntryDef 0x38 +CNavBlockedQuadTreeNode 0x10 +CNavNavigableLeafNode 0x20 +CNavQuadTree 0xb0 +CNavQuadTreeNode 0x20 +CNavSwitchableLeafNode 0x34 +CNavigatorBase 0x24 +CNavigatorFlyer 0x30 +CNavigatorManager 0x4c +CNetworkClient 0x2680 +CNymphDef 0x50 +CObserver 0x18 +COccupiableDef 0x2c +COracleMinigameDef 0x5c +COverheadDisplayDef 0x28 +CPackedUIntArray 0x10 +CParentDefClassBase 0x3d +CParticleEmitter 0x178 +CParticleProgram 0x170 +CParticleSimulationRenderData 0x4 +CPerceivedThingDef 0x50 +CPhysicsFacesSegmentBranch 0x1c +CPhysicsFacesSegmentLeaf 0x14 +CPixelShader 0x8 +CPixelShaderData 0x14 +CPlayer 0x234 +CPlayerGui 0x360 +CPlayerManager 0x34 +CPreallocTexturePool 0x54 +CQ_GuildTrainingPreMeleeScript 0x6c +CQuadTreeElement 0x4c +CQuestManager 0x114 +CReactionFrequencyTraits_ControlledCount 0x2c +CReactionFrequencyTraits_LinearTime 0x10 +CReactionFrequencyTraits_PeriodicFrequency 0x20 +CReactionMatch_AsymmetricPyramid 0x28 +CReactionMatch_MRPyramid 0x34 +CReactionMatch_SecondaryAxisCentredMRShifted 0x24 +CReactionMatch_SecondaryAxisMRShifted 0x24 +CReactionMatch_Sphere 0x1c +CReadableDef 0x26 +CRegion 0x64 +CRegionDialog 0x144 +CRenderManager 0x3ea4 +CRenderManager2D 0x3ea4 +CRenderManagerCore 0x3d20 +CRenderStateManager 0x3a3c +CRenderTarget 0x24 +CResource 0x1c +CResourceList 0x3c +CResurrectionItemDef 0x2c +CSB 0xc +CSBAgents 0x10 +CSBCAgents 0x1c +CSBCCorridor 0x34 +CSBGoal 0x24 +CSBIAgents 0x18 +CScriptConversation 0x34 +CScriptGameResourceObjectScriptedThing 0x28 +CScriptGameResourceObjectScriptedThingBase 0x10 +CScriptThing 0xc +CScriptedMapBrush 0x64 +CScrollingComponent 0x18c +CShaderBankEntry 0x24 +CShaderBankStateBlock 0xc +CShaderBase 0x4 +CShaderDataBank 0x1a8 +CShipDef 0x44 +CShopItemDef 0x48 +CSimpleMemoryPool 0x1c +CSmashableDef 0x30 +CSnowTrollDef 0x60 +CSpawnedFuncBase 0x34 +CSpecialAbilitiesForcePushDef 0xb4 +CSpecialAbilitiesPhysicalShieldDef 0x16c +CSpotLightDef 0x44 +CSprite 0x1b4 +CStartupDefinitionManager 0xf0 +CStateBlockFunctionBase 0x4 +CStateBlockFunctionSold 0x4 +CStaticFontBank 0x2034 +CStaticMapBankFile 0x164 +CStaticMapBankStateBlock 0x8 +CStealthDef 0x48 +CStockItemDef 0x40 +CStreamingFontBank 0x3034 +CStreamingFontBankStateBlock 0x14 +CStreamingFontDataBank 0x190 +CStreamingFontDataBankEntry 0x18 +CStringParser 0x30 +CSubtitleRenderer 0x128 +CSummonDef 0x4c +CSummonerDef 0x5c +CSurface 0x10 +CSystemFont 0x8 +CTCActivationReceptorBase 0x30 +CTCBase 0xc +CTCBerserk 0x78 +CTCBoastUI 0x34 +CTCBoastingPosition 0x38 +CTCBulletTime 0x58 +CTCBuyableHouse 0x4c +CTCCombat 0xac +CTCCrateStack 0x20 +CTCCreatureGenerator 0xc4 +CTCCreatureGroupBoastingCrowd 0x4c +CTCCreatureOpinionOfHero 0x65c +CTCDoor 0x24 +CTCEntityEvents 0x18 +CTCExplodingObject 0x30 +CTCHeroAttachableAppearanceModifiers 0x58 +CTCHeroCentre 0x3c +CTCHeroExperience 0x7c +CTCHeroRangedAttacks 0x4c +CTCHeroStats 0x1fc +CTCHighlightItem 0x28 +CTCInGameMenu 0x118 +CTCInventory 0x270 +CTCInventoryAbilities 0x220 +CTCInventoryClothing 0x198 +CTCInventoryExperience 0x2d8 +CTCInventoryItem 0x28 +CTCInventoryTrade 0x280 +CTCMapwho 0x54 +CTCNamedUserActionPoints 0x4c +CTCOracleMinigame 0xf4 +CTCPerceiveThing 0x58 +CTCPhysicsBase 0x40 +CTCPhysicsNavigator 0xb0 +CTCPhysicsRigidBody 0x270 +CTCPhysicsStandard 0x88 +CTCQuestCard 0x34 +CTCRegionDisplay 0x28 +CTCShotDefinitionProjectile 0xa8 +CTCSpotLight 0x2c +CTCStealth 0x50 +CTCStockItem 0x1c +CTCTargeted 0x18 +CTCTargetingPlayer 0x100 +CTCTavernGame 0x108 +CTCTrapBase 0x6c +CTCTurncoat 0x50 +CTCTurncoatSpell 0x9c +CTCVillage 0x174 +CTCVolumeContainmentTrackerDef 0x30 +CTCWeapon 0x28 +CTCWife 0xf4 +CTCWillLightning 0xf0 +CTable 0x1a4 +CTattooDef 0x60 +CTavernDef 0x2c +CTavernGameCardBaseDef 0x8c +CTavernGameCoinBaseDef 0x44 +CTavernGameCoinGolfDef 0x5c +CTavernTableDef 0x27 +CTestPos 0x24 +CText 0x1bc +CTexture 0x8 +CTextureManager 0x5d4 +CThingAICreature 0x210 +CThingCreatureBase 0x188 +CThingDrainLifeShotDef 0x3c +CThingGameObject 0xe8 +CThingMapManager 0x44 +CThingMultiArrowShotDef 0x54 +CThingPhysical 0xd8 +CThingSearchTools 0x1c +CThreadedFileObjectDesc 0x18 +CTimeAppearanceFadeDef 0x38 +CTrophyDef 0x64 +CVertexBufferWin32 0x34 +CVertexShader 0x8 +CVertexShaderConstantLayout 0x150 +CVertexShaderConstantLayoutEmpty 0x150 +CVertexShaderData 0x14 +CVideoSys 0x94 +CVillageMemberDef 0x26 +CVillageTaskCarryCrate 0x20 +CVillageTaskHorn 0x18 +CVillageTaskPatrol 0x1c +CVillageTaskPickCrops 0x18 +CVillageTaskSecuritySweep 0x34 +CVillageTaskTorch 0x18 +CVirtualKeyboard 0x10 +CVsyncCallbackTimer 0x14 +CWADBank 0x1d8 +CWADBankStateBlock 0x8 +CWADFile 0x10 +CWaterPatchMesh 0x20 +CWideString 0x4 +CWideStringData 0x10 +CWillResponseDef 0x2d +CWorld 0x1b8 +CWorldMap 0xfc +ConfigDetection 0x2c3c +LHHttp2 0x1104 +z_stream_s 0x38 diff --git a/docs/modern/X86_LAYOUT_DRIFT.tsv b/docs/modern/X86_LAYOUT_DRIFT.tsv new file mode 100644 index 0000000000..dab77be5cf --- /dev/null +++ b/docs/modern/X86_LAYOUT_DRIFT.tsv @@ -0,0 +1 @@ +class pdb_size_x86 diff --git a/rebuild/modern/CMakeLists.txt b/rebuild/modern/CMakeLists.txt index 9d4bbc8eb2..b33abc2b45 100644 --- a/rebuild/modern/CMakeLists.txt +++ b/rebuild/modern/CMakeLists.txt @@ -1,7 +1,13 @@ cmake_minimum_required(VERSION 3.25) -project(FableTLCModern LANGUAGES CXX) +project(FableTLCModern LANGUAGES C CXX) include(CTest) add_subdirectory(multiplayer) +add_subdirectory(codec) +add_subdirectory(compat) +add_subdirectory(platform) +add_subdirectory(bank) +add_subdirectory(save) +add_subdirectory(texture) diff --git a/rebuild/modern/README.md b/rebuild/modern/README.md index 21b547a248..1d0246b87d 100644 --- a/rebuild/modern/README.md +++ b/rebuild/modern/README.md @@ -27,3 +27,19 @@ See `docs/ARCHITECTURE.md` for the promotion and modernization policy. codec for individual cut-co-op `CGameEvent` records, with boundary and stream tests. Package framing and gameplay dispatch remain future evidence-gated work. +- `save/` — retail saves and profiles: the `FableSave!` container (zlib, retail load gates), + every body section typed (HEADER, clock, PLAYER, ENTITIES cells, NPC names, REGIONS, + QUESTS/FACTIONS text and boasts) and `Profile.bin`. Every game-written save (402) and profile + (153) re-encodes byte-identically. LZO for the text blobs is an optional GPL download. See + `save/README.md`. +- `codec/` — the tree's own LZO1X decoder (MIT), shared by `save/` and `texture/`. +- `texture/` — GBANK textures (descriptor, LZO1X mip 0, A8R8G8B8/DXT1/DXT3 to RGBA, PNG). It + decodes 394/394 frontend and 6,319/6,324 world textures. `fable_title_view` presents the + retail title logo in a 64-bit window through D3D12 or Vulkan (pixel-identical captures): the + modern tree's first frame. See `texture/README.md`. +- `compat/` — types that keep retail (VC7.1) layouts under current compilers (`retail_bitset`). +- `platform/` — step-3 platform services; first: mouse input via Raw Input with a per-tick + accumulator (no 256-record limit). See `platform/README.md`. +- `bank/` — the BIGB `.big` bank container (TOCs, payload views, minimal-repack writer). All 17 + bank files in the install round-trip byte-identically, and resize/revert edits hold. See + `bank/README.md`. diff --git a/rebuild/modern/bank/CMakeLists.txt b/rebuild/modern/bank/CMakeLists.txt new file mode 100644 index 0000000000..e1f756d40f --- /dev/null +++ b/rebuild/modern/bank/CMakeLists.txt @@ -0,0 +1,38 @@ +add_library(fable_bank + src/BigBank.cpp +) + +target_include_directories(fable_bank + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/include" +) + +target_compile_features(fable_bank PUBLIC cxx_std_23) +set_target_properties(fable_bank PROPERTIES + CXX_EXTENSIONS OFF +) + +if(MSVC) + set(FABLE_BANK_WARNINGS /W4 /permissive-) +else() + set(FABLE_BANK_WARNINGS -Wall -Wextra -Wconversion -Wpedantic) +endif() +target_compile_options(fable_bank PRIVATE ${FABLE_BANK_WARNINGS}) + +add_executable(fable_bank_check + tools/fable_bank_check.cpp +) +target_link_libraries(fable_bank_check PRIVATE fable_bank) +target_compile_options(fable_bank_check PRIVATE ${FABLE_BANK_WARNINGS}) + +if(BUILD_TESTING) + add_executable(fable_bank_tests + tests/BigBankTests.cpp + ) + target_link_libraries(fable_bank_tests PRIVATE fable_bank) + target_compile_options(fable_bank_tests PRIVATE ${FABLE_BANK_WARNINGS}) + + add_test(NAME fable_bank.BigBank + COMMAND fable_bank_tests + ) +endif() diff --git a/rebuild/modern/bank/README.md b/rebuild/modern/bank/README.md new file mode 100644 index 0000000000..f452b5d012 --- /dev/null +++ b/rebuild/modern/bank/README.md @@ -0,0 +1,51 @@ +# Bank archives (`.big`) + +Portable C++23 codec for the retail BIGB bank container ([BIG.md](../../../docs/formats/BIG.md)). +It holds textures, meshes, animations, text, fonts, lipsync, effects and shaders. This is the +fork-side "bank headers" item of MODERNIZATION step 2 (x64 hazard "Persisted layouts"). Every +field is an explicit little-endian `u32`, so nothing depends on pointer width or on retail struct +images. + +## Model + +- `BigBank`: version, then sub-banks in footer order. +- `SubBank`: name, version, alignment, the optional `(type, count)` stats header, entries. +- `BankEntry`: magic, ID, type, CRC (a per-format class fingerprint), name and dependency strings + as raw on-disk bytes (retail length prefixes include the NUL; tool-added entries may not), + timestamp, info sub-header and payload. +- `Payload`: a view into the file the bank was read from, or owned replacement bytes. A 533 MB + `textures.big` is not copied to rewrite one texture. + +## Writer + +`encode()` lays out every region (TOCs and non-empty payloads) in its original address order, +using the minimal-repack rule of BIG.md §9.1. Each region starts on `snap_up(cursor, B)`, where +`B` is the largest power of two, at most the file's largest sub-bank alignment, that divides the +region's original start. The footer follows the last region, and padding is zero. An unmodified +bank therefore re-encodes byte for byte. A resized payload moves only the regions after it, and +each keeps its alignment. + +## Verified (2026-10-17, MSVC x64 and GCC 15; MSVC x86 on the files up to 250 MB) + +`fable_bank_check --edit ` over the install's `data` folder: 17 bank files (the 8 retail +`.big` files plus 9 tool-written copies and backups, up to 535 MB). + +- **17/17 decode and re-encode byte-identically.** +- In every sub-bank, one payload is grown by the file's largest alignment plus one. The result + decodes, only that payload changed, and every payload that was aligned stays aligned. + Restoring the original bytes gives back the original file in 16/16 aligned files. +- The live `textures.big` was rewritten by the texture-upscale tool with its payloads packed + unaligned (6,321 of 6,325). It still round-trips exactly. But edit-then-revert cannot be + byte-exact there: a re-decode can only re-derive boundaries from the shifted offsets. The check + reports it as `exact+edit(packed)` and requires only that the reverted bank decodes. + +`fable_bank_tests` covers a new bank's round trip, empty payloads, a resize ripple with +alignment, grow-then-revert, and the magic, truncation and footer-range gates. + +## Not covered + +- Payload formats (texture, mesh, animation, text, lipsync decoders): those are per bank kind; + see BIG.md §4. +- Adding new entries or sub-banks. A bank built from scratch lays out in insertion order with + every region on the largest alignment, which the engine accepts (the Python `big_write.rebuild(adds=)` + path loads in-engine), but that path is not exercised against the engine from this codec. diff --git a/rebuild/modern/bank/include/fable/bank/BigBank.hpp b/rebuild/modern/bank/include/fable/bank/BigBank.hpp new file mode 100644 index 0000000000..e54af1adef --- /dev/null +++ b/rebuild/modern/bank/include/fable/bank/BigBank.hpp @@ -0,0 +1,113 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fable::bank { + +enum class BigError { + bad_magic, // not "BIGB" + truncated, // a field or region runs past the end of the file + footer_out_of_range, + bad_toc, // a TOC record does not parse, or its parsed length differs from the footer's size + payload_out_of_range, + overlap, // two regions claim the same bytes (encode: a layout the writer cannot place) + too_large, // an offset or size no longer fits the 32-bit fields +}; + +// An entry's payload: a view into the bank file it was read from, or bytes that replace it. +// Keeping the view avoids copying the large banks (textures.big is 533 MB) to rewrite them. +class Payload final { +public: + Payload() = default; + Payload(std::shared_ptr> source, std::size_t offset, std::size_t size) + : source_(std::move(source)), offset_(offset), size_(size) {} + explicit Payload(std::vector bytes) + : owned_(std::make_shared>(std::move(bytes))), size_(owned_->size()) {} + + [[nodiscard]] std::span bytes() const noexcept + { + if (owned_) { + return *owned_; + } + if (source_) { + return std::span(*source_).subspan(offset_, size_); + } + return {}; + } + [[nodiscard]] std::size_t size() const noexcept { return size_; } + + friend bool operator==(const Payload& a, const Payload& b) + { + const auto x = a.bytes(); + const auto y = b.bytes(); + return x.size() == y.size() && std::equal(x.begin(), x.end(), y.begin()); + } + +private: + std::shared_ptr> source_; + std::shared_ptr> owned_; + std::size_t offset_ = 0; + std::size_t size_ = 0; +}; + +// One TOC record. Strings are kept as their raw on-disk bytes: retail length prefixes include a +// trailing NUL, entries added by later tools may not, and the engine accepts both. +struct BankEntry { + std::uint32_t magic = 42; + std::uint32_t id = 0; + std::int32_t type = 0; + std::uint32_t crc = 0; // a per-format class fingerprint, not a payload hash (BIG.md §8.1) + std::vector name; // raw, as stored after its u32 length + std::uint32_t timestamp = 0; + std::vector> deps; + std::vector info; + Payload payload; + // Where the payload sat in the file it was read from. The writer keeps regions in this + // order and on the alignment boundary they had (BIG.md §9.1). For an empty payload this is + // the stored offset field, written back unchanged. + std::uint64_t original_offset = 0; + + [[nodiscard]] std::string_view name_text() const noexcept; + friend bool operator==(const BankEntry&, const BankEntry&) = default; +}; + +struct SubBank { + std::string name; + std::uint32_t version = 0; + std::uint32_t align = 1; + // (type, count) pairs at the head of the TOC; nullopt when the TOC has none. + std::optional>> stats; + std::vector entries; + std::uint64_t original_toc_offset = 0; + + friend bool operator==(const SubBank&, const SubBank&) = default; +}; + +// A `.big` bank archive (BIG.md §2): 16-byte header, payloads and per-sub-bank TOCs in any +// address order, then the sub-bank directory (footer). +struct BigBank { + std::uint32_t version = 100; + std::vector banks; + std::uint64_t original_footer_offset = 0; + + friend bool operator==(const BigBank&, const BigBank&) = default; + + // `file` is kept alive by the payload views. + [[nodiscard]] static std::expected decode(std::shared_ptr> file); + + // Re-lays every region in its original address order with the minimal-repack rule, so an + // unmodified bank re-encodes byte for byte and a resized payload ripples only what follows it. + [[nodiscard]] std::expected, BigError> encode() const; +}; + +} // namespace fable::bank diff --git a/rebuild/modern/bank/src/BigBank.cpp b/rebuild/modern/bank/src/BigBank.cpp new file mode 100644 index 0000000000..1461e3d000 --- /dev/null +++ b/rebuild/modern/bank/src/BigBank.cpp @@ -0,0 +1,357 @@ +#include + +#include +#include +#include + +namespace fable::bank { +namespace { + +constexpr std::size_t header_size = 16; +constexpr std::uint32_t max_stats_count = 1000; // EgoCore's rewind rule (BIG.md §2) +constexpr std::size_t min_entry_size = 40; // ten u32 fields, excluding counted bytes/dependencies + +class Reader final { +public: + Reader(std::span bytes, std::size_t pos) : bytes_(bytes), pos_(pos) {} + + [[nodiscard]] std::size_t position() const noexcept { return pos_; } + void seek(std::size_t pos) noexcept { pos_ = pos; } + + bool u32(std::uint32_t& value) + { + if (bytes_.size() < 4 || pos_ > bytes_.size() - 4) { + return false; + } + value = 0; + for (int i = 0; i < 4; ++i) { + value |= std::to_integer(bytes_[pos_ + static_cast(i)]) << (8 * i); + } + pos_ += 4; + return true; + } + + bool raw(std::size_t count, std::vector& out) + { + if (count > bytes_.size() - pos_) { + return false; + } + out.assign(bytes_.begin() + static_cast(pos_), + bytes_.begin() + static_cast(pos_ + count)); + pos_ += count; + return true; + } + + // u32 length, then that many bytes. + bool counted(std::vector& out) + { + std::uint32_t length = 0; + return u32(length) && raw(length, out); + } + + bool cstring(std::string& out) + { + for (std::size_t i = pos_; i < bytes_.size(); ++i) { + if (bytes_[i] == std::byte{0}) { + out.assign(reinterpret_cast(bytes_.data()) + pos_, i - pos_); + pos_ = i + 1; + return true; + } + } + return false; + } + +private: + std::span bytes_; + std::size_t pos_ = 0; +}; + +class Writer final { +public: + void u32(std::uint32_t value) + { + for (int i = 0; i < 4; ++i) { + bytes_.push_back(static_cast((value >> (8 * i)) & 0xFFU)); + } + } + void raw(std::span bytes) { bytes_.insert(bytes_.end(), bytes.begin(), bytes.end()); } + void counted(std::span bytes) + { + u32(static_cast(bytes.size())); + raw(bytes); + } + [[nodiscard]] std::vector take() noexcept { return std::move(bytes_); } + +private: + std::vector bytes_; +}; + +struct FooterRecord { + std::string name; + std::uint32_t version = 0; + std::uint32_t entry_count = 0; + std::uint32_t toc_offset = 0; + std::uint32_t toc_size = 0; + std::uint32_t align = 0; +}; + +// One TOC, with each payload's offset taken from `offsets` (index-parallel to the entries). +std::vector encode_toc(const SubBank& bank, const std::vector& offsets) +{ + Writer out; + if (bank.stats) { + out.u32(static_cast(bank.stats->size())); + for (const auto& [type, count] : *bank.stats) { + out.u32(type); + out.u32(count); + } + } + for (std::size_t i = 0; i < bank.entries.size(); ++i) { + const BankEntry& entry = bank.entries[i]; + out.u32(entry.magic); + out.u32(entry.id); + out.u32(static_cast(entry.type)); + out.u32(static_cast(entry.payload.size())); + out.u32(static_cast(offsets[i])); + out.u32(entry.crc); + out.counted(entry.name); + out.u32(entry.timestamp); + out.u32(static_cast(entry.deps.size())); + for (const auto& dep : entry.deps) { + out.counted(dep); + } + out.counted(entry.info); + } + return out.take(); +} + +// The largest power of two <= cap that divides x: the boundary the bank builder placed a region on. +std::uint64_t original_boundary(std::uint64_t x, std::uint64_t cap) +{ + std::uint64_t b = 1; + while (b * 2 <= cap && x % (b * 2) == 0) { + b *= 2; + } + return b; +} + +std::uint64_t snap_up(std::uint64_t x, std::uint64_t boundary) +{ + return (x + boundary - 1) / boundary * boundary; +} + +} // namespace + +std::string_view BankEntry::name_text() const noexcept +{ + std::string_view text(reinterpret_cast(name.data()), name.size()); + while (!text.empty() && text.back() == '\0') { + text.remove_suffix(1); + } + return text; +} + +std::expected BigBank::decode(std::shared_ptr> file) +{ + if (!file) { + return std::unexpected(BigError::truncated); + } + const std::span bytes(*file); + if (bytes.size() < header_size) { + return std::unexpected(BigError::truncated); + } + if (bytes[0] != std::byte{'B'} || bytes[1] != std::byte{'I'} || bytes[2] != std::byte{'G'} || bytes[3] != std::byte{'B'}) { + return std::unexpected(BigError::bad_magic); + } + Reader head(bytes, 4); + BigBank out; + std::uint32_t footer_offset = 0; + std::uint32_t footer_size = 0; + head.u32(out.version); + head.u32(footer_offset); + head.u32(footer_size); + if (footer_offset < header_size || footer_offset > bytes.size() || footer_size > bytes.size() - footer_offset) { + return std::unexpected(BigError::footer_out_of_range); + } + out.original_footer_offset = footer_offset; + + Reader footer(bytes.first(static_cast(footer_offset) + footer_size), footer_offset); + std::uint32_t bank_count = 0; + if (!footer.u32(bank_count)) { + return std::unexpected(BigError::truncated); + } + std::vector records; + for (std::uint32_t i = 0; i < bank_count; ++i) { + FooterRecord r; + if (!footer.cstring(r.name) || !footer.u32(r.version) || !footer.u32(r.entry_count) || !footer.u32(r.toc_offset) + || !footer.u32(r.toc_size) || !footer.u32(r.align)) { + return std::unexpected(BigError::truncated); + } + records.push_back(std::move(r)); + } + if (footer.position() != static_cast(footer_offset) + footer_size) { + return std::unexpected(BigError::footer_out_of_range); + } + + for (const FooterRecord& r : records) { + if (r.toc_offset > bytes.size() || r.toc_size > bytes.size() - r.toc_offset) { + return std::unexpected(BigError::bad_toc); + } + const auto toc_end = static_cast(r.toc_offset) + r.toc_size; + Reader toc(bytes.first(toc_end), r.toc_offset); + SubBank bank; + bank.name = r.name; + bank.version = r.version; + bank.align = r.align; + bank.original_toc_offset = r.toc_offset; + + std::uint32_t stats_count = 0; + if (!toc.u32(stats_count)) { + return std::unexpected(BigError::bad_toc); + } + if (stats_count < max_stats_count) { + bank.stats.emplace(); + for (std::uint32_t i = 0; i < stats_count; ++i) { + std::uint32_t type = 0; + std::uint32_t count = 0; + if (!toc.u32(type) || !toc.u32(count)) { + return std::unexpected(BigError::bad_toc); + } + bank.stats->emplace_back(type, count); + } + } else { + toc.seek(r.toc_offset); + } + + if (r.entry_count > (toc_end - toc.position()) / min_entry_size) { + return std::unexpected(BigError::bad_toc); + } + bank.entries.reserve(r.entry_count); + for (std::uint32_t i = 0; i < r.entry_count; ++i) { + BankEntry entry; + std::uint32_t type = 0; + std::uint32_t size = 0; + std::uint32_t offset = 0; + std::uint32_t dep_count = 0; + if (!toc.u32(entry.magic) || !toc.u32(entry.id) || !toc.u32(type) || !toc.u32(size) || !toc.u32(offset) + || !toc.u32(entry.crc) || !toc.counted(entry.name) || !toc.u32(entry.timestamp) || !toc.u32(dep_count)) { + return std::unexpected(BigError::bad_toc); + } + if (dep_count > r.toc_size / 4) { + return std::unexpected(BigError::bad_toc); + } + entry.deps.resize(dep_count); + for (auto& dep : entry.deps) { + if (!toc.counted(dep)) { + return std::unexpected(BigError::bad_toc); + } + } + if (!toc.counted(entry.info)) { + return std::unexpected(BigError::bad_toc); + } + entry.type = static_cast(type); + entry.original_offset = offset; + if (size != 0) { + if (offset < header_size || offset > bytes.size() || size > bytes.size() - offset) { + return std::unexpected(BigError::payload_out_of_range); + } + entry.payload = Payload(file, offset, size); + } + bank.entries.push_back(std::move(entry)); + } + if (toc.position() != toc_end) { + return std::unexpected(BigError::bad_toc); + } + out.banks.push_back(std::move(bank)); + } + return out; +} + +std::expected, BigError> BigBank::encode() const +{ + // Regions in original address order: each TOC, each non-empty payload, then the footer. + struct Region { + std::uint64_t original_start; + std::size_t bank; + std::size_t entry; // npos for a TOC + std::uint64_t size; + std::uint64_t start = 0; + }; + constexpr std::size_t toc_region = static_cast(-1); + + std::uint64_t cap = 1; + for (const SubBank& bank : banks) { + cap = std::max(cap, bank.align); + } + + // TOC lengths do not depend on the offset values, so they can be sized before layout. + std::vector> offsets(banks.size()); + std::vector regions; + for (std::size_t b = 0; b < banks.size(); ++b) { + offsets[b].resize(banks[b].entries.size()); + const auto toc_size = encode_toc(banks[b], offsets[b]).size(); + regions.push_back(Region{banks[b].original_toc_offset, b, toc_region, toc_size}); + for (std::size_t e = 0; e < banks[b].entries.size(); ++e) { + const BankEntry& entry = banks[b].entries[e]; + offsets[b][e] = entry.original_offset; + if (entry.payload.size() != 0) { + regions.push_back(Region{entry.original_offset, b, e, entry.payload.size()}); + } + } + } + std::stable_sort(regions.begin(), regions.end(), + [](const Region& x, const Region& y) { return x.original_start < y.original_start; }); + + std::uint64_t cursor = header_size; + for (Region& region : regions) { + region.start = snap_up(cursor, original_boundary(region.original_start, cap)); + cursor = region.start + region.size; + if (region.entry != toc_region) { + offsets[region.bank][region.entry] = region.start; + } + } + + // The footer follows the last region (BIG.md §5: it follows the last TOC contiguously). + Writer footer; + footer.u32(static_cast(banks.size())); + std::vector> tocs(banks.size()); + for (std::size_t b = 0; b < banks.size(); ++b) { + tocs[b] = encode_toc(banks[b], offsets[b]); + } + for (std::size_t b = 0; b < banks.size(); ++b) { + const auto toc = std::find_if(regions.begin(), regions.end(), + [&](const Region& r) { return r.bank == b && r.entry == toc_region; }); + const SubBank& bank = banks[b]; + footer.raw(std::span(reinterpret_cast(bank.name.data()), bank.name.size())); + footer.raw(std::array{std::byte{0}}); + footer.u32(bank.version); + footer.u32(static_cast(bank.entries.size())); + footer.u32(static_cast(toc->start)); + footer.u32(static_cast(tocs[b].size())); + footer.u32(bank.align); + } + const auto footer_bytes = footer.take(); + const std::uint64_t footer_offset = cursor; + const std::uint64_t total = footer_offset + footer_bytes.size(); + if (total > std::numeric_limits::max()) { + return std::unexpected(BigError::too_large); + } + + std::vector out(static_cast(total), std::byte{0}); + Writer header; + header.raw(std::array{std::byte{'B'}, std::byte{'I'}, std::byte{'G'}, std::byte{'B'}}); + header.u32(version); + header.u32(static_cast(footer_offset)); + header.u32(static_cast(footer_bytes.size())); + const auto header_bytes = header.take(); + std::copy(header_bytes.begin(), header_bytes.end(), out.begin()); + for (const Region& region : regions) { + const auto bytes = region.entry == toc_region ? std::span(tocs[region.bank]) + : banks[region.bank].entries[region.entry].payload.bytes(); + std::copy(bytes.begin(), bytes.end(), out.begin() + static_cast(region.start)); + } + std::copy(footer_bytes.begin(), footer_bytes.end(), out.begin() + static_cast(footer_offset)); + return out; +} + +} // namespace fable::bank diff --git a/rebuild/modern/bank/tests/BigBankTests.cpp b/rebuild/modern/bank/tests/BigBankTests.cpp new file mode 100644 index 0000000000..459713b471 --- /dev/null +++ b/rebuild/modern/bank/tests/BigBankTests.cpp @@ -0,0 +1,182 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using fable::bank::BankEntry; +using fable::bank::BigBank; +using fable::bank::Payload; +using fable::bank::SubBank; + +int failures = 0; + +void expect(const bool condition, const std::string_view message) +{ + if (!condition) { + std::cerr << "FAIL: " << message << '\n'; + ++failures; + } +} + +std::vector bytes(const std::string_view text) +{ + std::vector out; + for (const char c : text) { + out.push_back(static_cast(c)); + } + return out; +} + +std::uint32_t u32_at(const std::vector& b, const std::size_t at) +{ + std::uint32_t v = 0; + for (std::size_t i = 0; i < 4; ++i) { + v |= std::to_integer(b[at + i]) << (8 * i); + } + return v; +} + +BankEntry entry(const std::uint32_t id, const std::string_view name, const std::string_view payload) +{ + BankEntry e; + e.id = id; + e.type = 1; + e.crc = 0x1234; + e.name = bytes(name); + e.name.push_back(std::byte{0}); // retail length prefixes include the NUL + e.deps.push_back(bytes("\\Dev\\source.tga")); + e.info = bytes("INFO"); + if (!payload.empty()) { + e.payload = Payload(bytes(payload)); + } + return e; +} + +BigBank sample() +{ + BigBank bank; + SubBank text; + text.name = "TEXT_ENGLISH_MAIN"; + text.version = 106; + text.align = 1; + text.stats = std::vector>{{1, 2}}; + text.entries.push_back(entry(1, "HELLO", "hello world")); + text.entries.push_back(entry(2, "EMPTY", "")); + SubBank tex; + tex.name = "GBANK_MAIN_PC"; + tex.version = 1; + tex.align = 16; + tex.stats = std::vector>{{0, 1}}; + tex.entries.push_back(entry(7, "TEX", "pixels")); + bank.banks = {text, tex}; + return bank; +} + +void round_trip_is_exact() +{ + const auto first = sample().encode(); + expect(first.has_value(), "a new bank encodes"); + if (!first) { + return; + } + expect(std::to_integer((*first)[0]) == 'B' && u32_at(*first, 4) == 100, "BIGB header, version 100"); + const auto file = std::make_shared>(*first); + const auto decoded = BigBank::decode(file); + expect(decoded.has_value(), "the encoded bank decodes"); + if (!decoded) { + return; + } + expect(decoded->banks.size() == 2 && decoded->banks[0].entries[0].name_text() == "HELLO", "names survive"); + expect(decoded->banks[0].entries[1].payload.size() == 0, "empty payloads stay empty"); + const auto again = decoded->encode(); + expect(again.has_value() && *again == *first, "decode then encode is byte-identical"); + for (const auto& sub : decoded->banks) { + for (const auto& e : sub.entries) { + expect(e.payload.size() == 0 || e.original_offset % 16 == 0, "regions sit on the file's largest alignment"); + } + } +} + +void resize_ripples_and_reverts() +{ + const auto file = std::make_shared>(*sample().encode()); + auto bank = *BigBank::decode(file); + bank.banks[0].entries[0].payload = Payload(bytes("hello world, now longer than before")); + const auto grown = bank.encode(); + const auto reread = BigBank::decode(std::make_shared>(*grown)); + expect(reread.has_value() && reread->banks[0].entries[0].payload == bank.banks[0].entries[0].payload, + "the resized payload reads back"); + expect(reread.has_value() && reread->banks[1].entries[0].payload == Payload(bytes("pixels")), + "the other payloads are unchanged"); + expect(reread.has_value() && reread->banks[1].entries[0].original_offset % 16 == 0, "rippled payloads stay aligned"); + auto reverted = *reread; + reverted.banks[0].entries[0].payload = Payload(bytes("hello world")); + const auto restored = reverted.encode(); + expect(restored.has_value() && *restored == *file, "grow then revert restores the original bytes"); +} + +void gates_are_enforced() +{ + auto bad = std::make_shared>(*sample().encode()); + (*bad)[0] = std::byte{'X'}; + expect(BigBank::decode(bad).error() == fable::bank::BigError::bad_magic, "bad magic is rejected"); + auto short_file = std::make_shared>(bytes("BIGB")); + expect(!BigBank::decode(short_file).has_value(), "a truncated header is rejected"); + auto footer = std::make_shared>(*sample().encode()); + (*footer)[8] = std::byte{0xFF}; + (*footer)[9] = std::byte{0xFF}; + (*footer)[10] = std::byte{0xFF}; + expect(BigBank::decode(footer).error() == fable::bank::BigError::footer_out_of_range, "a footer past the end is rejected"); +} + +void entry_counts_are_bounded_before_reserving() +{ + auto malformed = std::make_shared>(*sample().encode()); + const std::size_t footer = u32_at(*malformed, 8); + const std::size_t count_at = footer + 4 + sample().banks[0].name.size() + 1 + 4; + for (std::size_t i = 0; i < 4; ++i) { + (*malformed)[count_at + i] = std::byte{0xff}; + } + try { + const auto decoded = BigBank::decode(malformed); + expect(!decoded && decoded.error() == fable::bank::BigError::bad_toc, + "an impossible entry count is rejected before allocation"); + } catch (const std::exception&) { + expect(false, "an impossible entry count must return bad_toc, not throw"); + } + + BigBank smallest; + SubBank sub; + sub.name = "EMPTY_FIELDS"; + sub.stats.emplace(); + sub.entries.emplace_back(); + smallest.banks.push_back(sub); + const auto encoded = smallest.encode(); + const auto decoded = BigBank::decode(std::make_shared>(*encoded)); + expect(decoded && decoded->banks[0].entries.size() == 1, + "a minimum-size forty-byte entry is still accepted"); + const auto null_file = BigBank::decode(nullptr); + expect(!null_file && null_file.error() == fable::bank::BigError::truncated, + "a null file is rejected as empty input"); +} + +} // namespace + +int main() +{ + round_trip_is_exact(); + resize_ripples_and_reverts(); + gates_are_enforced(); + entry_counts_are_bounded_before_reserving(); + if (failures == 0) { + std::cout << "fable_bank: all tests passed\n"; + } + return failures == 0 ? 0 : 1; +} diff --git a/rebuild/modern/bank/tools/fable_bank_check.cpp b/rebuild/modern/bank/tools/fable_bank_check.cpp new file mode 100644 index 0000000000..78e12dc13b --- /dev/null +++ b/rebuild/modern/bank/tools/fable_bank_check.cpp @@ -0,0 +1,157 @@ +// fable_bank_check [--edit] ... +// +// For each `.big` (BIGB) bank: decode, re-encode, and report whether the rewrite is byte-identical. +// With --edit, also grow one payload in every sub-bank (by one byte more than the file's largest +// alignment), check that the result decodes with only that payload changed and every payload on +// its sub-bank's alignment, then restore the original bytes and check that the file comes back +// exactly (the writer's invertibility, BIG.md §9.1). Exit status 1 on any failure. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using fable::bank::BigBank; +using fable::bank::Payload; + +std::shared_ptr> read_file(const std::filesystem::path& path) +{ + std::ifstream in(path, std::ios::binary); + auto bytes = std::make_shared>(static_cast(std::filesystem::file_size(path))); + in.read(reinterpret_cast(bytes->data()), static_cast(bytes->size())); + return bytes; +} + +bool has_magic(const std::filesystem::path& path) +{ + std::ifstream in(path, std::ios::binary); + char magic[4] = {}; + in.read(magic, 4); + return in && std::equal(magic, magic + 4, "BIGB"); +} + +// "" if the edit round trip holds, else what broke. +std::string edit_round_trip(const BigBank& bank, const std::vector& original, const bool packed) +{ + std::uint32_t cap = 1; + for (const auto& sub : bank.banks) { + cap = std::max(cap, sub.align); + } + for (std::size_t b = 0; b < bank.banks.size(); ++b) { + const auto& entries = bank.banks[b].entries; + const auto target = std::find_if(entries.begin(), entries.end(), [](const auto& e) { return e.payload.size() != 0; }); + if (target == entries.end()) { + continue; + } + const auto e = static_cast(target - entries.begin()); + BigBank edited = bank; + std::vector grown(target->payload.bytes().begin(), target->payload.bytes().end()); + grown.resize(grown.size() + cap + 1, std::byte{0x5A}); + edited.banks[b].entries[e].payload = Payload(grown); + + const auto bytes = edited.encode(); + if (!bytes) { + return "edit encode failed in " + bank.banks[b].name; + } + const auto again = BigBank::decode(std::make_shared>(*bytes)); + if (!again) { + return "edited bank does not decode (" + bank.banks[b].name + ")"; + } + for (std::size_t bb = 0; bb < bank.banks.size(); ++bb) { + const auto& sub = again->banks[bb]; + for (std::size_t ee = 0; ee < sub.entries.size(); ++ee) { + const auto& got = sub.entries[ee]; + const auto& want = edited.banks[bb].entries[ee]; + if (got.payload != want.payload || got.name != want.name || got.info != want.info || got.crc != want.crc) { + return "edit changed another entry in " + sub.name; + } + // Only payloads the original file had aligned must stay aligned: tool-written banks + // (e.g. a repacked textures.big) can store payloads off their sub-bank boundary. + const bool was_aligned = bank.banks[bb].entries[ee].original_offset % sub.align == 0; + if (got.payload.size() != 0 && was_aligned && got.original_offset % sub.align != 0) { + return "edit broke alignment in " + sub.name; + } + } + } + BigBank reverted = *again; + reverted.banks[b].entries[e].payload = Payload(std::vector(target->payload.bytes().begin(), target->payload.bytes().end())); + const auto restored = reverted.encode(); + // Invertibility needs the original boundaries, which a re-decode recovers only when the + // payloads were aligned: in a packed bank they are re-derived from the shifted offsets. + if (packed) { + if (!restored || !BigBank::decode(std::make_shared>(*restored))) { + return "reverted packed bank does not decode (" + bank.banks[b].name + ")"; + } + } else if (!restored || *restored != original) { + return "grow-then-revert is not the original (" + bank.banks[b].name + ")"; + } + } + return ""; +} + +} // namespace + +int main(int argc, char** argv) +{ + bool edit = false; + std::vector files; + for (int i = 1; i < argc; ++i) { + const std::string arg = argv[i]; + if (arg == "--edit") { + edit = true; + } else if (std::filesystem::is_directory(arg)) { + for (const auto& entry : std::filesystem::recursive_directory_iterator(arg)) { + if (entry.is_regular_file() && has_magic(entry.path())) { + files.push_back(entry.path()); + } + } + } else { + files.push_back(arg); + } + } + + int failures = 0; + for (const auto& path : files) { + const auto file = read_file(path); + const auto bank = BigBank::decode(file); + std::string verdict; + std::size_t entries = 0; + std::size_t unaligned = 0; + if (!bank) { + verdict = "rejected_" + std::to_string(static_cast(bank.error())); + } else { + for (const auto& sub : bank->banks) { + entries += sub.entries.size(); + for (const auto& e : sub.entries) { + unaligned += e.payload.size() != 0 && e.original_offset % sub.align != 0; + } + } + const auto encoded = bank->encode(); + if (!encoded) { + verdict = "encode_failed_" + std::to_string(static_cast(encoded.error())); + } else if (*encoded != *file) { + verdict = "encode_mismatch"; + } else { + verdict = "exact"; + if (edit) { + const auto problem = edit_round_trip(*bank, *file, unaligned != 0); + verdict = !problem.empty() ? "edit:" + problem : unaligned != 0 ? "exact+edit(packed)" : "exact+edit"; + } + } + } + if (verdict.rfind("exact", 0) != 0) { + ++failures; + } + std::cout << verdict << '\t' << (bank ? bank->banks.size() : 0) << " banks\t" << entries << " entries\t" + << unaligned << " unaligned\t" << file->size() << " bytes\t" << path.string() << '\n'; + } + return failures == 0 ? 0 : 1; +} diff --git a/rebuild/modern/codec/CMakeLists.txt b/rebuild/modern/codec/CMakeLists.txt new file mode 100644 index 0000000000..d1252a0914 --- /dev/null +++ b/rebuild/modern/codec/CMakeLists.txt @@ -0,0 +1,30 @@ +add_library(fable_codec + src/Lzo1x.cpp + src/Lzo1xCompress.cpp +) + +target_include_directories(fable_codec + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/include" +) + +target_compile_features(fable_codec PUBLIC cxx_std_23) +set_target_properties(fable_codec PROPERTIES + CXX_EXTENSIONS OFF +) + +if(MSVC) + target_compile_options(fable_codec PRIVATE /W4 /permissive-) +else() + target_compile_options(fable_codec PRIVATE -Wall -Wextra -Wconversion -Wpedantic) +endif() + +if(BUILD_TESTING) + add_executable(fable_codec_tests + tests/Lzo1xTests.cpp + ) + target_link_libraries(fable_codec_tests PRIVATE fable_codec) + add_test(NAME fable_codec.Lzo1x + COMMAND fable_codec_tests + ) +endif() diff --git a/rebuild/modern/codec/include/fable/codec/Lzo1x.hpp b/rebuild/modern/codec/include/fable/codec/Lzo1x.hpp new file mode 100644 index 0000000000..0542605acb --- /dev/null +++ b/rebuild/modern/codec/include/fable/codec/Lzo1x.hpp @@ -0,0 +1,28 @@ +#pragma once + +// LZO1X stream decoder (this tree's own implementation of the format, MIT). Used by save text +// blobs, texture mip 0 and the other bank payloads that retail compresses with lzo1x_999. + +#include +#include +#include +#include + +namespace fable::codec { + +enum class Lzo1xError { + truncated, // the stream ends inside a token, a literal run or a match + bad_distance, // a match reaches back before the start of the output + size_mismatch, // the stream decodes to a different size than expected + trailing_bytes, // bytes follow the end-of-stream marker +}; + +[[nodiscard]] std::expected, Lzo1xError> lzo1x_decompress( + std::span stream, std::size_t expected_size); + +// LZO1X encoder (MIT, ported from FableForge): hash-chain matches and a cost-based parse over the +// exact grammar. Valid LZO1X for any decoder, the retail engine's included, but not +// lzo1x_999's exact bytes (byte-identical rewrites still need upstream LZO; see save/README). +[[nodiscard]] std::vector lzo1x_compress(std::span data); + +} // namespace fable::codec diff --git a/rebuild/modern/codec/src/Lzo1x.cpp b/rebuild/modern/codec/src/Lzo1x.cpp new file mode 100644 index 0000000000..ce7b4772e0 --- /dev/null +++ b/rebuild/modern/codec/src/Lzo1x.cpp @@ -0,0 +1,240 @@ +#include + +#include +#include + +namespace fable::codec { +namespace { + +// LZO1X stream decoder. Token grammar (t = the token byte): +// first byte > 17 initial literal run of t - 17 bytes +// t < 16 at loop top literal run of t + 3 bytes (t == 0: zero-byte extension, base 15) +// t >= 64 M2: length (t >> 5) + 1, distance 1 + ((t >> 2) & 7) + (next << 3) +// t >= 32 M3: length (t & 31) + 2 (extension base 31), 14-bit distance + 1 +// t >= 16 M4: length (t & 7) + 2 (extension base 7), distance 0x4000 + ...; +// an encoded distance of 0 is the end of the stream +// t < 16 after a run a 3-byte match at distance 0x801 + (t >> 2) + (next << 2) +// t < 16 after a match a 2-byte match at distance 1 + (t >> 2) + (next << 2) +// After every match, the low two bits of the byte two before the input position give 0-3 +// literals to copy; a nonzero count is followed directly by the next match token. +class Decoder final { +public: + Decoder(std::span in, std::size_t expected) : in_(in), expected_(expected) + { + out_.reserve(std::min(expected, in.size())); + } + + [[nodiscard]] bool size_exceeded() const noexcept { return size_exceeded_; } + + std::expected, Lzo1xError> run() + { + enum class State { loop, after_run, match, after_match }; + State state = State::loop; + std::size_t t = 0; + + if (!in_.empty() && byte_at(0) > 17) { + t = byte_at(0) - 17; + ip_ = 1; + if (!literals(t)) { + return std::unexpected(Lzo1xError::truncated); + } + state = State::after_run; + if (t < 4) { + // A short initial run plays the role of a match's trailing literals. + if (!next(t)) { + return std::unexpected(Lzo1xError::truncated); + } + state = State::match; + } + } + + for (;;) { + switch (state) { + case State::loop: { + if (!next(t)) { + return std::unexpected(Lzo1xError::truncated); + } + if (t >= 16) { + state = State::match; + break; + } + if (t == 0 && !extend(t, 15)) { + return std::unexpected(Lzo1xError::truncated); + } + if (!literals(t + 3)) { + return std::unexpected(Lzo1xError::truncated); + } + state = State::after_run; + break; + } + case State::after_run: { + std::size_t h = 0; + if (!next(t)) { + return std::unexpected(Lzo1xError::truncated); + } + if (t >= 16) { + state = State::match; + break; + } + if (!next(h)) { + return std::unexpected(Lzo1xError::truncated); + } + if (!copy_match(0x801 + (t >> 2) + (h << 2), 3)) { + return std::unexpected(Lzo1xError::bad_distance); + } + state = State::after_match; + break; + } + case State::match: { + std::size_t distance = 0; + std::size_t length = 0; + std::size_t h = 0; + if (t >= 64) { + if (!next(h)) { + return std::unexpected(Lzo1xError::truncated); + } + distance = 1 + ((t >> 2) & 7) + (h << 3); + length = (t >> 5) + 1; + } else if (t >= 32) { + std::size_t n = t & 31; + if (n == 0 && !extend(n, 31)) { + return std::unexpected(Lzo1xError::truncated); + } + std::size_t lo = 0; + if (!next(lo) || !next(h)) { + return std::unexpected(Lzo1xError::truncated); + } + distance = 1 + ((h << 6) | (lo >> 2)); + length = n + 2; + } else if (t >= 16) { + std::size_t n = t & 7; + if (n == 0 && !extend(n, 7)) { + return std::unexpected(Lzo1xError::truncated); + } + std::size_t lo = 0; + if (!next(lo) || !next(h)) { + return std::unexpected(Lzo1xError::truncated); + } + const std::size_t encoded = ((t & 8) << 11) + ((h << 6) | (lo >> 2)); + if (encoded == 0) { + return finish(); + } + distance = 0x4000 + encoded; + length = n + 2; + } else { + if (!next(h)) { + return std::unexpected(Lzo1xError::truncated); + } + distance = 1 + (t >> 2) + (h << 2); + length = 2; + } + if (!copy_match(distance, length)) { + return std::unexpected(Lzo1xError::bad_distance); + } + state = State::after_match; + break; + } + case State::after_match: { + const std::size_t trailing = byte_at(ip_ - 2) & 3; + if (trailing == 0) { + state = State::loop; + break; + } + if (!literals(trailing) || !next(t)) { + return std::unexpected(Lzo1xError::truncated); + } + state = State::match; + break; + } + } + } + } + +private: + std::size_t byte_at(std::size_t at) const { return std::to_integer(in_[at]); } + + bool next(std::size_t& value) + { + if (ip_ >= in_.size()) { + return false; + } + value = byte_at(ip_++); + return true; + } + + // Zero bytes add 255 each; the first nonzero byte adds itself plus `base`. + bool extend(std::size_t& value, std::size_t base) + { + std::size_t total = 0; + while (ip_ < in_.size() && in_[ip_] == std::byte{0}) { + total += 255; + ++ip_; + } + std::size_t last = 0; + if (!next(last)) { + return false; + } + value = total + base + last; + return true; + } + + bool literals(std::size_t count) + { + if (count > in_.size() - ip_) { + return false; + } + if (count > expected_ - out_.size()) { + size_exceeded_ = true; + return false; + } + out_.insert(out_.end(), in_.begin() + static_cast(ip_), + in_.begin() + static_cast(ip_ + count)); + ip_ += count; + return true; + } + + bool copy_match(std::size_t distance, std::size_t length) + { + if (distance > out_.size()) { + return false; + } + if (length > expected_ - out_.size()) { + size_exceeded_ = true; + return false; + } + std::size_t from = out_.size() - distance; + for (std::size_t i = 0; i < length; ++i) { + out_.push_back(out_[from++]); // overlapping copies repeat, as in the format + } + return true; + } + + std::expected, Lzo1xError> finish() + { + if (ip_ != in_.size()) { + return std::unexpected(Lzo1xError::trailing_bytes); + } + return std::move(out_); + } + + std::span in_; + std::size_t expected_ = 0; + bool size_exceeded_ = false; + std::size_t ip_ = 0; + std::vector out_; +}; + +} // namespace + +std::expected, Lzo1xError> lzo1x_decompress(std::span stream, + std::size_t expected_size) +{ + Decoder decoder(stream, expected_size); + auto out = decoder.run(); + if (decoder.size_exceeded() || (out && out->size() != expected_size)) { + return std::unexpected(Lzo1xError::size_mismatch); + } + return out; +} + +} // namespace fable::codec diff --git a/rebuild/modern/codec/src/Lzo1xCompress.cpp b/rebuild/modern/codec/src/Lzo1xCompress.cpp new file mode 100644 index 0000000000..7bdbec8e89 --- /dev/null +++ b/rebuild/modern/codec/src/Lzo1xCompress.cpp @@ -0,0 +1,303 @@ +#include + +#include +#include +#include +#include + +// LZO1X encoder, ported unchanged in substance from FableForge `src/lzo1x.cpp` (MIT, by the +// same author as this tree; FableForge commit a358de0). Its output is not lzo1x_999's exact +// bytes, but any LZO1X decoder accepts it, the retail engine's included: FableForge verifies it +// through minilzo and its own clean-room decoder and ships it for in-game STB and texture edits. + +namespace fable::codec { +namespace { + +using std::size_t; +using std::uint8_t; +using std::uint32_t; + +// ---------------------------------------------------------------- encoder +// +// Two stages. A hash-chain match finder records, for every position, the +// longest match overall and the longest within each cheaper distance class +// (M2 <= 2048, M3 <= 16384), the nearest 3-byte match in the M1L band +// (2049..3072) and whether a 2-byte match exists within 1024 (M1). Then a +// backward dynamic programme over the exact grammar: literal runs cost their +// bytes plus the run token (free for 0..3 literals riding in the previous +// match token, +1 for 4..18, +1 and length extension beyond), and the token +// that may follow a run depends on the run's length class: +// after 0 literals : M2 / M3 / M4 +// after 1..3 literals : also M1 (2 bytes at distance 1..1024, 2 bytes) +// after a run >= 4 : also M1L (3 bytes at distance 2049..3072, 2 bytes) +// which is exactly why lzo1x_999 output is smaller than a plain parse. The +// forward pass emits tokens in that grammar. The point is size: retail STB +// frames were written with lzo1x_999 and an edited frame must fit its slot. + +namespace { + +struct Cand { uint32_t dist = 0, len = 0; }; +struct Cands { + Cand best, m2, m3; // longest overall / within 2048 / within 16384 + uint32_t m1lDist = 0; // 3-byte match at 2049..3072 (0 = none) + uint32_t m1Dist = 0; // 2-byte match at 1..1024 (0 = none) +}; + +constexpr size_t kMaxDist = 0xBFFF; +constexpr size_t kMinLen = 3; +constexpr size_t kNiceLen = 512; +constexpr uint32_t kChainDepth = 400; +constexpr uint32_t kInf = 0x3FFFFFFFu; + +inline uint32_t extBytes(size_t rem) { return uint32_t((rem - 1) / 255 + 1); } + +// bytes for an M2/M3/M4 token of `len` at `dist` (no literal-run bytes) +inline uint32_t tokenCost(size_t len, size_t dist) { + if (dist <= 2048 && len <= 8) return 2; + if (dist <= 16384) { const size_t t = len - 2; return 3 + (t > 31 ? extBytes(t - 31) : 0); } + const size_t t = len - 2; + return 3 + (t > 7 ? extBytes(t - 7) : 0); +} + +void findMatches(const uint8_t* in, size_t n, std::vector& out) { + out.assign(n, Cands{}); + if (n < 2) return; + // 2-byte pairs: last position of each pair, for M1 + std::vector pairLast(65536, 0xFFFFFFFFu); + for (size_t i = 0; i + 2 <= n; ++i) { + const uint32_t key = uint32_t(in[i]) | (uint32_t(in[i + 1]) << 8); + const uint32_t p = pairLast[key]; + if (p != 0xFFFFFFFFu && i - p <= 1024) out[i].m1Dist = uint32_t(i - p); + pairLast[key] = uint32_t(i); + } + if (n < kMinLen) return; + constexpr size_t kHashBits = 16; + std::vector head(size_t(1) << kHashBits, 0xFFFFFFFFu); + std::vector next(n, 0xFFFFFFFFu); + // Positions advance monotonically. A match at distance d proves equality + // up to its end for every later position inside that span at the same d. + // Reuse that verified prefix without changing the longest-match result. + std::vector matchEnd(std::min(n,kMaxDist+1),0); + auto hash = [&](size_t i) { + const uint32_t v = uint32_t(in[i]) | (uint32_t(in[i + 1]) << 8) | (uint32_t(in[i + 2]) << 16); + return size_t((v * 0x9E3779B1u) >> (32 - kHashBits)); + }; + for (size_t i = 0; i + kMinLen <= n; ++i) { + const size_t h = hash(i); + uint32_t p = head[h]; + Cands& c = out[i]; + uint32_t depth = 0; + const size_t maxLen = n - i; + while (p != 0xFFFFFFFFu && depth++ < kChainDepth) { + const size_t dist = i - p; + if (dist > kMaxDist) break; + const bool canImprove = c.best.len < maxLen && in[p + c.best.len] == in[i + c.best.len]; + const bool wantClass = (dist <= 16384 && c.m3.len < maxLen) || (dist >= 2049 && dist <= 3072 && !c.m1lDist); + if (canImprove || wantClass) { + size_t len = matchEnd[dist]>i ? matchEnd[dist]-i : 0; + while (len < maxLen && in[p + len] == in[i + len]) ++len; + matchEnd[dist]=i+len; + if (len >= kMinLen) { + if (len > c.best.len) c.best = {uint32_t(dist), uint32_t(len)}; + if (dist <= 2048 && len > c.m2.len) c.m2 = {uint32_t(dist), uint32_t(len)}; + if (dist <= 16384 && len > c.m3.len) c.m3 = {uint32_t(dist), uint32_t(len)}; + if (dist >= 2049 && dist <= 3072 && !c.m1lDist) c.m1lDist = uint32_t(dist); + if (len >= kNiceLen) break; + } + } + p = next[p]; + } + next[i] = head[h]; + head[h] = uint32_t(i); + } +} + +} // namespace + +} // namespace + +namespace { + +std::vector compress_bytes(const uint8_t* in, size_t n) { + std::vector out; + out.reserve(n / 2 + 16); + if (n == 0) { out = {0x11, 0x00, 0x00}; return out; } + + std::vector cands; + findMatches(in, n, cands); + + // A[i] : cheapest cost of in[i..n) starting right after a match token (0 literals so far) + // B[c][i]: cheapest cost of in[i..n) starting with a match token in literal context c + // (0: no literals before, 1: 1..3 literals before, 2: run >= 4 before) + // L[i] : cheapest cost of in[i..n) while inside a literal run that is already + // >= 19 long (each further literal costs 1; ending it means a class-2 match) + // Choices are packed for the forward pass. + struct Choice { uint32_t len = 0, dist = 0; uint8_t kind = 0; }; // kind: 0 M2/3/4, 1 M1, 2 M1L + std::vector A(n + 1, kInf), L(n + 1, kInf); + std::vector B[3] = {std::vector(n + 1, kInf), std::vector(n + 1, kInf), std::vector(n + 1, kInf)}; + std::vector Bc[3] = {std::vector(n + 1), std::vector(n + 1), std::vector(n + 1)}; + std::vector Ak(n + 1, 0); // literal count chosen at A[i]; 0xFFFFFFFF = long run + constexpr uint32_t kLong = 0xFFFFFFFFu; + // end marker costs 3 in every context; the tail literals are a plain run + A[n] = 3; L[n] = 3; + for (int c = 0; c < 3; ++c) B[c][n] = 3; + + auto runCost = [](size_t k) -> uint32_t { // bytes for a literal run of k (token + literals) + if (k == 0) return 0; + if (k <= 3) return uint32_t(k); + if (k <= 18) return uint32_t(k + 1); + return uint32_t(k + 1 + extBytes(k - 18)); + }; + + for (size_t i = n; i-- > 0;) { + // ---- B: a match starts at i + const Cands& c = cands[i]; + Choice bestCh[3]; uint32_t bestV[3] = {kInf, kInf, kInf}; + auto consider = [&](const Cand& cd) { + if (cd.len < kMinLen) return; + auto tryLen = [&](size_t Lm) { + if (Lm < kMinLen || Lm > cd.len) return; + const uint32_t v = tokenCost(Lm, cd.dist) + A[i + Lm]; + for (int k = 0; k < 3; ++k) if (v < bestV[k]) { bestV[k] = v; bestCh[k] = {uint32_t(Lm), cd.dist, 0}; } + }; + tryLen(cd.len); + const size_t lo = cd.len > 24 ? cd.len - 24 : kMinLen; + for (size_t Lm = lo; Lm < cd.len; ++Lm) tryLen(Lm); + static const size_t thresholds[] = {3, 4, 8, 9, 33}; + for (size_t t : thresholds) if (t < lo) tryLen(t); + }; + consider(c.best); consider(c.m2); consider(c.m3); + if (c.m1Dist && i + 2 <= n) { // class 1 only + const uint32_t v = 2 + A[i + 2]; + if (v < bestV[1]) { bestV[1] = v; bestCh[1] = {2, c.m1Dist, 1}; } + } + if (c.m1lDist && i + 3 <= n) { // class 2 only + const uint32_t v = 2 + A[i + 3]; + if (v < bestV[2]) { bestV[2] = v; bestCh[2] = {3, c.m1lDist, 2}; } + } + for (int k = 0; k < 3; ++k) { B[k][i] = bestV[k]; Bc[k][i] = bestCh[k]; } + + // ---- L: inside a long literal run + L[i] = std::min(1 + L[i + 1], B[2][i]); + + // ---- A: choose how many literals precede the next match (or the end) + uint32_t best = kInf, bk = 0; + const size_t rem = n - i; + // finish with literals only + if (rem <= 18) { const uint32_t v = runCost(rem) + 3; if (v < best) { best = v; bk = uint32_t(rem); } } + if (B[0][i] < best) { best = B[0][i]; bk = 0; } + for (size_t k = 1; k <= 18 && k < rem; ++k) { + // The very first run of 1..3 literals ("17+k" byte) is followed by a + // plain match token in the C decoder but by an after-literal-run token + // in LZO's i386 assembly decoder (lzo1x_decompress_asm_fast, which + // Fable's landscape loader uses): the two disagree on tokens < 16 + // there, so only M2/M3/M4 (class 0) are allowed after it. + const int cls = (i == 0 && k <= 3) ? 0 : (k <= 3 ? 1 : 2); + const uint32_t v = runCost(k) + B[cls][i + k]; + if (v < best) { best = v; bk = uint32_t(k); } + } + if (rem >= 19) { + // long run: 19 literals (+ token + first extension byte) then keep going or match + const uint32_t v = 19 + 2 + L[i + 19]; + if (v < best) { best = v; bk = kLong; } + } + A[i] = best; Ak[i] = bk; + } + + // ---- forward emission + auto extend = [&](size_t remv) { while (remv > 255) { out.push_back(0); remv -= 255; } out.push_back(uint8_t(remv)); }; + size_t litBitsPos = SIZE_MAX; + bool first = true; + auto emitRun = [&](size_t start, size_t k) { // k literals in[start..start+k) + if (first) { + first = false; + if (k <= 3) out.push_back(uint8_t(17 + k)); + else if (k <= 18) out.push_back(uint8_t(k - 3)); + else { out.push_back(0); extend(k - 18); } + } else if (k == 0) { + } else if (k <= 3) { + out[litBitsPos] |= uint8_t(k); + } else if (k <= 18) { + out.push_back(uint8_t(k - 3)); + } else { + out.push_back(0); extend(k - 18); + } + out.insert(out.end(), in + start, in + start + k); + }; + auto emitMatch = [&](const Choice& ch) { + const size_t len = ch.len, dist = ch.dist; + if (ch.kind == 1) { // M1: 2 bytes, dist 1..1024 + const size_t d = dist - 1; + litBitsPos = out.size(); + out.push_back(uint8_t((d & 3) << 2)); + out.push_back(uint8_t(d >> 2)); + } else if (ch.kind == 2) { // M1L: 3 bytes, dist 2049..3072 + const size_t d = dist - 0x801; + litBitsPos = out.size(); + out.push_back(uint8_t((d & 3) << 2)); + out.push_back(uint8_t(d >> 2)); + } else if (dist <= 2048 && len <= 8) { // M2 + litBitsPos = out.size(); + out.push_back(uint8_t(((len - 1) << 5) | (((dist - 1) & 7) << 2))); + out.push_back(uint8_t((dist - 1) >> 3)); + } else if (dist <= 16384) { // M3 + const size_t t = len - 2; + if (t <= 31) out.push_back(uint8_t(0x20 | t)); + else { out.push_back(0x20); extend(t - 31); } + const size_t d = dist - 1; + litBitsPos = out.size(); + out.push_back(uint8_t((d & 0x3F) << 2)); + out.push_back(uint8_t(d >> 6)); + } else { // M4 + const size_t d = dist - 0x4000; + const uint8_t t0 = uint8_t(0x10 | ((d >> 11) & 8)); + const size_t t = len - 2; + if (t <= 7) out.push_back(uint8_t(t0 | t)); + else { out.push_back(t0); extend(t - 7); } + const size_t d14 = d & 0x3FFF; + litBitsPos = out.size(); + out.push_back(uint8_t((d14 & 0x3F) << 2)); + out.push_back(uint8_t(d14 >> 6)); + } + }; + + size_t i = 0; + while (i < n) { + uint32_t k = Ak[i]; + size_t runLen; + int cls; + if (k == kLong) { + // extend the run while L says "one more literal" is the choice + size_t j = i + 19; + while (j < n && 1 + L[j + 1] <= B[2][j]) ++j; + runLen = j - i; cls = 2; + } else { + runLen = k; cls = k == 0 ? 0 : (k <= 3 ? ((i == 0) ? 0 : 1) : 2); + } + emitRun(i, runLen); + i += runLen; + if (i >= n) break; + const Choice ch = Bc[cls][i]; + if (ch.len == 0) { // no match possible here (B == inf): cannot happen when A chose it; guard + emitRun(i, n - i); i = n; break; + } + emitMatch(ch); + i += ch.len; + } + out.push_back(0x11); out.push_back(0x00); out.push_back(0x00); // end marker + return out; +} + +} // namespace + +std::vector lzo1x_compress(std::span data) +{ + const auto bytes = compress_bytes(reinterpret_cast(data.data()), data.size()); + std::vector out(bytes.size()); + if (!bytes.empty()) { + std::memcpy(out.data(), bytes.data(), bytes.size()); + } + return out; +} + +} // namespace fable::codec diff --git a/rebuild/modern/codec/tests/Lzo1xTests.cpp b/rebuild/modern/codec/tests/Lzo1xTests.cpp new file mode 100644 index 0000000000..de88d2e314 --- /dev/null +++ b/rebuild/modern/codec/tests/Lzo1xTests.cpp @@ -0,0 +1,83 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +int failures = 0; + +std::vector bytes(const std::string& s) +{ + std::vector out; + for (const char c : s) { + out.push_back(static_cast(c)); + } + return out; +} + +void round_trip(const std::vector& data, const char* what) +{ + const auto packed = fable::codec::lzo1x_compress(data); + const auto back = fable::codec::lzo1x_decompress(packed, data.size()); + if (!back || *back != data) { + std::cerr << "FAIL: round trip of " << what << '\n'; + ++failures; + } +} + +} // namespace + +int main() +{ + // A missing end marker must not hide that a literal/match already exceeds + // the declared output. Reject before expanding the offending token. + for (const auto& stream : { + std::vector{std::byte{0x13}, std::byte{'a'}, std::byte{'b'}}, + std::vector{std::byte{0x12}, std::byte{'a'}, std::byte{0x21}, std::byte{0}, std::byte{0}}}) { + const auto result = fable::codec::lzo1x_decompress(stream, 1); + if (result || result.error() != fable::codec::Lzo1xError::size_mismatch) { + std::cerr << "FAIL: output limit is enforced before the next token\n"; + ++failures; + } + } + try { + const auto result = fable::codec::lzo1x_decompress({}, std::numeric_limits::max()); + if (result || result.error() != fable::codec::Lzo1xError::truncated) { + std::cerr << "FAIL: empty stream is truncated regardless of declared size\n"; + ++failures; + } + } catch (const std::exception&) { + std::cerr << "FAIL: declared size alone must not allocate the output\n"; + ++failures; + } + round_trip({}, "empty input"); + round_trip(bytes("ab"), "two bytes"); + round_trip(bytes("START_QUEST_CARDS\nEND_QUEST_CARDS\nSTART_ACTIVE_QUESTS\nEND_ACTIVE_QUESTS\n"), "quest text"); + std::string repeated; + for (int i = 0; i < 4000; ++i) { + repeated += "Name \"FACTION_" + std::to_string(i % 29) + "\";\n"; + } + round_trip(bytes(repeated), "repetitive text"); + std::vector noise; + std::uint32_t seed = 7; + for (int i = 0; i < 70000; ++i) { + seed = seed * 1664525U + 1013904223U; + noise.push_back(static_cast(seed >> 24)); + } + round_trip(noise, "incompressible data"); + const auto packed = fable::codec::lzo1x_compress(bytes(repeated)); + if (packed.size() * 4 > repeated.size()) { + std::cerr << "FAIL: repetitive text should compress at least 4x\n"; + ++failures; + } + if (failures == 0) { + std::cout << "fable_codec: all tests passed\n"; + } + return failures == 0 ? 0 : 1; +} diff --git a/rebuild/modern/compat/CMakeLists.txt b/rebuild/modern/compat/CMakeLists.txt new file mode 100644 index 0000000000..89687c74cb --- /dev/null +++ b/rebuild/modern/compat/CMakeLists.txt @@ -0,0 +1,14 @@ +# Header-only types that keep retail (VC7.1) layouts under current compilers. +add_library(fable_compat INTERFACE) +target_include_directories(fable_compat INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_compile_features(fable_compat INTERFACE cxx_std_17) + +if(BUILD_TESTING) + add_executable(fable_compat_tests + tests/RetailBitsetTests.cpp + ) + target_link_libraries(fable_compat_tests PRIVATE fable_compat) + add_test(NAME fable_compat.RetailBitset + COMMAND fable_compat_tests + ) +endif() diff --git a/rebuild/modern/compat/include/fable/compat/retail_bitset.hpp b/rebuild/modern/compat/include/fable/compat/retail_bitset.hpp new file mode 100644 index 0000000000..d1a93e85a9 --- /dev/null +++ b/rebuild/modern/compat/include/fable/compat/retail_bitset.hpp @@ -0,0 +1,125 @@ +#pragma once + +// std::bitset's interface over VC7.1's storage: ceil(N/32) 32-bit words, bit i in word i/32 at +// bit i%32. The reconstructed headers embed std::bitset in persisted/engine layouts (CThing:: +// UsingComponents, 274 bits = 36 bytes under VC7.1). Current standard libraries store 64-bit +// words (40 bytes for 274 bits), which moves every later member. The fork uses this type where +// a retail layout must hold (MODERN_BEHAVIOR_CENSUS, "retail bitset"). + +#include +#include +#include + +namespace fable::compat { + +template +class retail_bitset { +public: + class reference { + public: + reference(retail_bitset& owner, std::size_t pos) noexcept : owner_(owner), pos_(pos) {} + reference& operator=(bool value) noexcept + { + owner_.set(pos_, value); + return *this; + } + reference& operator=(const reference& other) noexcept { return *this = static_cast(other); } + operator bool() const noexcept { return owner_.test_unchecked(pos_); } + bool operator~() const noexcept { return !static_cast(*this); } + reference& flip() noexcept + { + owner_.flip(pos_); + return *this; + } + + private: + retail_bitset& owner_; + std::size_t pos_; + }; + + constexpr retail_bitset() noexcept = default; + + [[nodiscard]] constexpr std::size_t size() const noexcept { return N; } + + [[nodiscard]] bool test(std::size_t pos) const + { + if (pos >= N) { + throw std::out_of_range("retail_bitset::test"); + } + return test_unchecked(pos); + } + [[nodiscard]] bool operator[](std::size_t pos) const noexcept { return test_unchecked(pos); } + reference operator[](std::size_t pos) noexcept { return reference(*this, pos); } + + retail_bitset& set() noexcept + { + for (auto& w : words_) { + w = 0xFFFFFFFFU; + } + trim(); + return *this; + } + retail_bitset& set(std::size_t pos, bool value = true) + { + if (pos >= N) { + throw std::out_of_range("retail_bitset::set"); + } + const std::uint32_t bit = std::uint32_t{1} << (pos % 32); + words_[pos / 32] = value ? (words_[pos / 32] | bit) : (words_[pos / 32] & ~bit); + return *this; + } + retail_bitset& reset() noexcept + { + for (auto& w : words_) { + w = 0; + } + return *this; + } + retail_bitset& reset(std::size_t pos) { return set(pos, false); } + retail_bitset& flip(std::size_t pos) { return set(pos, !test(pos)); } + + [[nodiscard]] std::size_t count() const noexcept + { + std::size_t n = 0; + for (std::uint32_t w : words_) { + for (; w; w &= w - 1) { + ++n; + } + } + return n; + } + [[nodiscard]] bool any() const noexcept { return count() != 0; } + [[nodiscard]] bool none() const noexcept { return count() == 0; } + + // Written out (not defaulted) so the header also builds as C++17, like the reconstructed code. + friend bool operator==(const retail_bitset& a, const retail_bitset& b) noexcept + { + for (std::size_t i = 0; i < word_count; ++i) { + if (a.words_[i] != b.words_[i]) { + return false; + } + } + return true; + } + friend bool operator!=(const retail_bitset& a, const retail_bitset& b) noexcept { return !(a == b); } + +private: + static constexpr std::size_t word_count = (N + 31) / 32; + + [[nodiscard]] bool test_unchecked(std::size_t pos) const noexcept + { + return ((words_[pos / 32] >> (pos % 32)) & 1U) != 0; + } + void trim() noexcept + { + if constexpr (N % 32 != 0) { + words_[word_count - 1] &= (std::uint32_t{1} << (N % 32)) - 1; + } + } + + std::uint32_t words_[word_count]{}; +}; + +static_assert(sizeof(retail_bitset<274>) == 36, "VC7.1 stores std::bitset<274> in 9 32-bit words"); + +} // namespace fable::compat diff --git a/rebuild/modern/compat/tests/RetailBitsetTests.cpp b/rebuild/modern/compat/tests/RetailBitsetTests.cpp new file mode 100644 index 0000000000..9a2a20c11b --- /dev/null +++ b/rebuild/modern/compat/tests/RetailBitsetTests.cpp @@ -0,0 +1,48 @@ +#include + +#include +#include +#include +#include + +int main() +{ + int failures = 0; + auto expect = [&](bool ok, const char* what) { + if (!ok) { + std::cerr << "FAIL: " << what << '\n'; + ++failures; + } + }; + fable::compat::retail_bitset<274> bits; + expect(sizeof(bits) == 36, "274 bits take VC7.1's 36 bytes"); + bits.set(0); + bits.set(33); + bits.set(273); + expect(bits.test(0) && bits.test(33) && bits.test(273) && !bits.test(1), "set/test"); + std::uint32_t words[9]; + std::memcpy(words, &bits, sizeof(words)); + expect(words[0] == 1 && words[1] == 2 && words[8] == (1U << 17), "bit i lives in 32-bit word i/32"); + bits.reset(33); + expect(!bits[33] && bits.count() == 2, "reset/count"); + bits[5] = true; + expect(bits.test(5), "reference assignment"); + bits.set(); + expect(bits.count() == 274, "set() fills exactly N bits"); + // Same observable behaviour as std::bitset for the operations the engine uses. + std::bitset<274> reference; + fable::compat::retail_bitset<274> mine; + for (std::size_t i = 0; i < 274; i += 7) { + reference.set(i); + mine.set(i); + } + bool same = true; + for (std::size_t i = 0; i < 274; ++i) { + same = same && reference.test(i) == mine.test(i); + } + expect(same && reference.count() == mine.count(), "matches std::bitset"); + if (failures == 0) { + std::cout << "fable_compat: all tests passed\n"; + } + return failures == 0 ? 0 : 1; +} diff --git a/rebuild/modern/platform/CMakeLists.txt b/rebuild/modern/platform/CMakeLists.txt new file mode 100644 index 0000000000..82c48d7b76 --- /dev/null +++ b/rebuild/modern/platform/CMakeLists.txt @@ -0,0 +1,51 @@ +# Platform services for the fork (MODERNIZATION step 3). First piece: mouse input. +add_library(fable_platform INTERFACE) +target_include_directories(fable_platform INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_compile_features(fable_platform INTERFACE cxx_std_23) + +if(WIN32) + add_library(fable_platform_win32 + src/RawMouseWin32.cpp + src/XInputPad.cpp + ) + target_link_libraries(fable_platform_win32 PUBLIC fable_platform PRIVATE user32) + add_executable(fable_rawmouse_probe + tools/fable_rawmouse_probe.cpp + ) + target_link_libraries(fable_rawmouse_probe PRIVATE fable_platform_win32) + add_executable(fable_pad_probe + tools/fable_pad_probe.cpp + ) + target_link_libraries(fable_pad_probe PRIVATE fable_platform_win32) +endif() + +if(BUILD_TESTING) + add_executable(fable_platform_tests + tests/MouseAccumulatorTests.cpp + ) + target_link_libraries(fable_platform_tests PRIVATE fable_platform) + add_test(NAME fable_platform.MouseAccumulator + COMMAND fable_platform_tests + ) + add_executable(fable_worldclock_tests + tests/WorldClockTests.cpp + ) + target_link_libraries(fable_worldclock_tests PRIVATE fable_platform) + add_test(NAME fable_platform.WorldClock + COMMAND fable_worldclock_tests + ) + add_executable(fable_pad_tests + tests/PadTests.cpp + ) + target_link_libraries(fable_pad_tests PRIVATE fable_platform) + add_test(NAME fable_platform.Pad + COMMAND fable_pad_tests + ) + add_executable(fable_keyboard_tests + tests/KeyboardAccumulatorTests.cpp + ) + target_link_libraries(fable_keyboard_tests PRIVATE fable_platform) + add_test(NAME fable_platform.Keyboard + COMMAND fable_keyboard_tests + ) +endif() diff --git a/rebuild/modern/platform/README.md b/rebuild/modern/platform/README.md new file mode 100644 index 0000000000..b19f3f9a8b --- /dev/null +++ b/rebuild/modern/platform/README.md @@ -0,0 +1,48 @@ +# Platform services (fork step 3) + +The fork's replacements for retail's Win32/DirectX platform code, behind small interfaces. First +piece: **mouse input**. + +- `MouseAccumulator` (portable, header-only): each OS report is folded into running sums as it + arrives, and the world tick takes one `MouseFrame` (summed dx/dy, wheel, held and pressed + buttons, report count). There is no fixed buffer, so polling rate cannot drop motion. Retail's + `CMouseDX` reads a 256-record DirectInput buffer once per 15 Hz world frame, and 4-8 kHz mice + overflow it (docs/modern/FRAME_TIMING.md "Live results"). +- `RawMouseWin32`: Win32 Raw Input (`WM_INPUT`) feeding the accumulator. +- `KeyboardAccumulator` + `RawKeyboardWin32`: what retail's `CKeyboardDX::Update` (0xAB69F0) gives + the game per tick, i.e. the held-key snapshot (`GetDeviceState`) plus the ordered transitions since + the last tick (buffered `GetDeviceData`), so a tap between two ticks still counts. Auto-repeat is + not an event; focus loss releases held keys. + +- `WorldClock` (portable, header-only): retail's world-tick scheduler. A world frame is due when + `predicted_present * 15 - frame > 1` (`IsTimeForServerUpdate` 0x41674A); `predicted_present` is the + next vblank after the queued frames, never earlier than now (0x4166E2). Each Update runs at most + one world frame, and the renderer draws at `predicted_present * 15 - frame`. + +- `Pad` + `XInputPad`: pads through XInput, with retail's stick semantics. Retail's `CJoystickDX` + (DirectInput) sets range 0..0x10000 and a **20 % dead zone on the left stick only** (EnumDevices + callback 0xAB6B10), and stores axes as `2*raw/65536 - 1` with +y down (UpdateEvents 0xAB6E40). + The fork keeps those values and reports the triggers separately; through DirectInput an Xbox + pad's triggers share one axis. `PadAccumulator` keeps presses between ticks. + +## Verified (2026-10-17) + +- `fable_platform_tests`: 2,000 reports in one tick are all summed; clicks inside one tick are + kept; held buttons carry over. +- `fable_worldclock_tests`: replays the retail live sample (60 Hz presents → 15 Hz world, 4 renders + per world frame) and keeps the world at 15 Hz at 144/165/240/1000 Hz refresh; a late frame + catches up one per Update. +- `fable_keyboard_tests`; live: 60 injected F24 taps (down and up within about 1 ms) gave 120 + events and 60 presses. +- `fable_pad_tests`: dead zone (centre inside 20 %, rescaled outside, continuous at the edge), + XInput range, a press between ticks kept. Live: an Xbox One Wireless Controller (Xbox Wireless + Adapter) shows in XInput slot 0, sticks at exactly 0 at rest after the dead zone. +- `fable_rawmouse_probe --seconds 12` while injecting 5 × 1,500 `SendInput` moves (the bursts + that made retail return `DI_BUFFEROVERFLOW`): 7,533 reports counted (7,500 injected, the rest + from the real mouse), busiest 15 Hz tick 459 reports, nothing dropped. + +## Not covered yet + +GameInput (newer pads), rumble, the real vblank source for `predicted_present`, the window, +files and config. See +MODERNIZATION step 3. diff --git a/rebuild/modern/platform/include/fable/platform/KeyboardAccumulator.hpp b/rebuild/modern/platform/include/fable/platform/KeyboardAccumulator.hpp new file mode 100644 index 0000000000..121c2b6719 --- /dev/null +++ b/rebuild/modern/platform/include/fable/platform/KeyboardAccumulator.hpp @@ -0,0 +1,64 @@ +#pragma once + +// The fork's keyboard model, matching what retail's CKeyboardDX gives the game each world tick +// (Update 0xAB69F0): the held-key snapshot (UpdateKeyStates 0xAB5FF0, GetDeviceState of 256 keys) +// and the ordered key events since the last tick (UpdateKeyEvents 0xAB6240, buffered +// GetDeviceData), so a tap that goes down and up between two 15 Hz ticks is still seen. +// Keys are Windows virtual-key codes (0-255). + +#include +#include +#include + +namespace fable::platform { + +struct KeyEvent { + std::uint8_t key = 0; // virtual-key code + bool down = false; +}; + +struct KeyboardFrame { + std::bitset<256> held; // keys down at the end of the tick + std::bitset<256> pressed; // keys that went down at least once during the tick + std::vector events; // every transition, in arrival order (auto-repeat excluded) +}; + +class KeyboardAccumulator { +public: + // One OS report. Auto-repeat (a down for a key already held) is not a new event. + void key(std::uint8_t vk, bool down) + { + if (down == held_.test(vk)) { + return; + } + held_.set(vk, down); + if (down) { + frame_.pressed.set(vk); + } + frame_.events.push_back(KeyEvent{vk, down}); + } + + // Focus loss: everything is released (retail re-acquires the device and re-reads its state). + void release_all() + { + for (unsigned vk = 0; vk < 256; ++vk) { + if (held_.test(vk)) { + key(static_cast(vk), false); + } + } + } + + [[nodiscard]] KeyboardFrame take() + { + KeyboardFrame out = std::move(frame_); + out.held = held_; + frame_ = KeyboardFrame{}; + return out; + } + +private: + KeyboardFrame frame_; + std::bitset<256> held_; +}; + +} // namespace fable::platform diff --git a/rebuild/modern/platform/include/fable/platform/MouseAccumulator.hpp b/rebuild/modern/platform/include/fable/platform/MouseAccumulator.hpp new file mode 100644 index 0000000000..bdc8ec0c69 --- /dev/null +++ b/rebuild/modern/platform/include/fable/platform/MouseAccumulator.hpp @@ -0,0 +1,66 @@ +#pragma once + +// The fork's mouse input model: every relative-motion report from the OS is added as it arrives, +// and the world tick takes one summed frame (MODERNIZATION step 3, input service). Retail's +// CMouseDX instead reads a 256-record DirectInput buffer once per 15 Hz world frame, which a +// 4-8 kHz mouse overflows (docs/modern/FRAME_TIMING.md "Live results"). Here there is no buffer +// to overflow: motion is folded into running sums immediately, whatever the polling rate. + +#include + +namespace fable::platform { + +struct MouseFrame { + std::int64_t dx = 0; // summed relative motion since the previous take() + std::int64_t dy = 0; + std::int32_t wheel = 0; // summed wheel delta (WHEEL_DELTA units) + std::uint32_t buttons_down = 0; // buttons held at the end of the frame (bit i = button i) + std::uint32_t buttons_pressed = 0; // buttons that went down at least once during the frame + std::uint64_t reports = 0; // OS reports folded into this frame +}; + +class MouseAccumulator { +public: + // One OS report: relative motion and the button transitions it carries. + void add_motion(std::int32_t dx, std::int32_t dy) noexcept + { + frame_.dx += dx; + frame_.dy += dy; + ++frame_.reports; + } + void add_wheel(std::int32_t delta) noexcept + { + frame_.wheel += delta; + ++frame_.reports; + } + void button(unsigned index, bool down) noexcept + { + if (index >= 32) { + return; + } + const std::uint32_t bit = std::uint32_t{1} << index; + if (down) { + frame_.buttons_pressed |= bit; + held_ |= bit; + } else { + held_ &= ~bit; + } + ++frame_.reports; + } + + // The world tick's read: everything since the last take(), then a fresh frame that keeps the + // held buttons. + [[nodiscard]] MouseFrame take() noexcept + { + MouseFrame out = frame_; + out.buttons_down = held_; + frame_ = MouseFrame{}; + return out; + } + +private: + MouseFrame frame_; + std::uint32_t held_ = 0; +}; + +} // namespace fable::platform diff --git a/rebuild/modern/platform/include/fable/platform/Pad.hpp b/rebuild/modern/platform/include/fable/platform/Pad.hpp new file mode 100644 index 0000000000..00182ba623 --- /dev/null +++ b/rebuild/modern/platform/include/fable/platform/Pad.hpp @@ -0,0 +1,78 @@ +#pragma once + +// The fork's game-pad model (MODERNIZATION step 3/4b). Retail reads pads through DirectInput +// (CJoystickDX): EnumDevices callback 0xAB6B10 sets DIPROP_RANGE 0..0x10000 and DIPROP_DEADZONE +// 2000 (20 %) on X and Y only, i.e. the left stick, plus a 64-event buffer; UpdateEvents 0xAB6E40 +// stores each axis as 2 * raw / 65536 - 1 (so Y grows downward, as DirectInput reports it). On an +// Xbox pad DirectInput also folds both triggers into one axis. The fork reads XInput instead and +// reproduces retail's stick values (same dead zone, same signs), while reporting the triggers +// separately. + +#include +#include + +namespace fable::platform { + +// Buttons in retail's EXboxControllerButton spirit; bit positions are the fork's own. +enum PadButton : std::uint32_t { + pad_a = 1U << 0, pad_b = 1U << 1, pad_x = 1U << 2, pad_y = 1U << 3, + pad_left_shoulder = 1U << 4, pad_right_shoulder = 1U << 5, + pad_back = 1U << 6, pad_start = 1U << 7, + pad_left_thumb = 1U << 8, pad_right_thumb = 1U << 9, + pad_dpad_up = 1U << 10, pad_dpad_down = 1U << 11, pad_dpad_left = 1U << 12, pad_dpad_right = 1U << 13, +}; + +struct PadState { + bool connected = false; + float left_x = 0, left_y = 0; // -1..1, retail convention: +x right, +y down; 20 % dead zone + float right_x = 0, right_y = 0; // -1..1, no dead zone (retail sets none on the right stick) + float left_trigger = 0, right_trigger = 0; // 0..1, separate (retail's DirectInput path shares one axis) + std::uint32_t buttons = 0; // held +}; + +// DirectInput's axial dead zone: inside it the axis reports centre, outside it the remaining +// travel is rescaled to the full range (so the value is continuous at the dead-zone edge). +[[nodiscard]] inline float retail_dead_zone(float value, float dead_zone = 0.2f) noexcept +{ + const float magnitude = std::fabs(value); + if (magnitude <= dead_zone) { + return 0.0f; + } + const float scaled = (magnitude - dead_zone) / (1.0f - dead_zone); + return std::copysign(scaled > 1.0f ? 1.0f : scaled, value); +} + +// XInput's signed 16-bit stick value to retail's -1..1 axis. +[[nodiscard]] inline float stick_axis(std::int16_t raw) noexcept +{ + return raw < 0 ? static_cast(raw) / 32768.0f : static_cast(raw) / 32767.0f; +} + +// Per-tick pad frame: buttons pressed at any poll since the last tick are kept, like the mouse and +// keyboard accumulators, so a quick press between two 15 Hz ticks is not lost. +struct PadFrame { + PadState state; // the latest poll + std::uint32_t pressed = 0; // buttons that went down during the tick +}; + +class PadAccumulator { +public: + void poll(const PadState& state) noexcept + { + frame_.pressed |= state.buttons & ~last_.buttons; + last_ = state; + } + [[nodiscard]] PadFrame take() noexcept + { + PadFrame out = frame_; + out.state = last_; + frame_ = PadFrame{}; + return out; + } + +private: + PadState last_; + PadFrame frame_; +}; + +} // namespace fable::platform diff --git a/rebuild/modern/platform/include/fable/platform/RawMouseWin32.hpp b/rebuild/modern/platform/include/fable/platform/RawMouseWin32.hpp new file mode 100644 index 0000000000..74f5e281bc --- /dev/null +++ b/rebuild/modern/platform/include/fable/platform/RawMouseWin32.hpp @@ -0,0 +1,42 @@ +#pragma once + +// Win32 Raw Input backend for MouseAccumulator. Register once for a window, then feed every +// WM_INPUT to handle(); each RAWMOUSE report goes straight into the accumulator, so the OS +// delivers every report at the device's polling rate and nothing is buffered on our side. + +#if defined(_WIN32) + +#include +#include + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include + +namespace fable::platform { + +class RawMouseWin32 { +public: + // Registers the generic mouse (usage page 1, usage 2) for `hwnd`. `background` keeps reports + // flowing while the window is not focused (RIDEV_INPUTSINK). + [[nodiscard]] static bool register_for(HWND hwnd, bool background = false) noexcept; + + // Call from the window procedure for WM_INPUT (lParam = HRAWINPUT). Returns true when the + // message was a mouse report. + static bool handle(LPARAM lparam, MouseAccumulator& into) noexcept; +}; + +// The keyboard counterpart: registers usage page 1 / usage 6 and feeds KeyboardAccumulator. +class RawKeyboardWin32 { +public: + [[nodiscard]] static bool register_for(HWND hwnd, bool background = false) noexcept; + static bool handle(LPARAM lparam, KeyboardAccumulator& into); +}; + +} // namespace fable::platform + +#endif diff --git a/rebuild/modern/platform/include/fable/platform/WorldClock.hpp b/rebuild/modern/platform/include/fable/platform/WorldClock.hpp new file mode 100644 index 0000000000..295865c495 --- /dev/null +++ b/rebuild/modern/platform/include/fable/platform/WorldClock.hpp @@ -0,0 +1,54 @@ +#pragma once + +// The world-tick scheduler, modelled on retail CMainGameComponent (docs/modern/FRAME_TIMING.md): +// +// GetCurrentGameTime 0x416231 = GFGetTime() - start (+0x60) +// GetGameTimeOfNextPresentCompletion 0x4166E2 +// = the next vblank after the frames already queued +// (CVsyncCallbackTimer), never earlier than now, - start +// IsTimeForServerUpdate(frame) 0x41674A = predicted_present * 15 - frame > 1.0 +// ticks per second = 15 (global 0x1375550) +// +// Each Update runs at most one world frame (a late frame catches up one per loop pass, not in a +// burst), and the renderer draws the world `predicted_present * 15 - frame` of the way into the +// next frame. Nothing here depends on the render rate: that is why retail's world runs at exactly +// 15 Hz with 4 renders per world frame at 60 Hz (live sample, FRAME_TIMING "Live results"). + +#include + +namespace fable::platform { + +class WorldClock { +public: + explicit WorldClock(double ticks_per_second = 15.0) noexcept : rate_(ticks_per_second) {} + + // IsTimeForServerUpdate: is the next world frame due for a present completing at `present` + // (seconds of game time)? + [[nodiscard]] bool due(double present) const noexcept { return present * rate_ - static_cast(frame_) > 1.0; } + + // Runs the bookkeeping of one world frame (the game advances its state alongside). + void advance() noexcept { ++frame_; } + + // The render fraction: how far past `frame` the predicted present is, in world frames. + [[nodiscard]] double interpolation(double present) const noexcept + { + return present * rate_ - static_cast(frame_); + } + + [[nodiscard]] std::uint64_t frame() const noexcept { return frame_; } + [[nodiscard]] double ticks_per_second() const noexcept { return rate_; } + +private: + double rate_; + std::uint64_t frame_ = 0; +}; + +// The present-time prediction: the vblank after `queued` already-submitted frames, or `now` if +// that would be in the past (0x4166E2's comparison against GFGetTime()). +[[nodiscard]] inline double predicted_present(double now, double last_vblank, double vblank_period, unsigned queued) noexcept +{ + const double predicted = last_vblank + static_cast(queued + 1) * vblank_period; + return predicted > now ? predicted : now; +} + +} // namespace fable::platform diff --git a/rebuild/modern/platform/include/fable/platform/XInputPad.hpp b/rebuild/modern/platform/include/fable/platform/XInputPad.hpp new file mode 100644 index 0000000000..ed879cf667 --- /dev/null +++ b/rebuild/modern/platform/include/fable/platform/XInputPad.hpp @@ -0,0 +1,24 @@ +#pragma once + +#if defined(_WIN32) + +#include + +#include + +namespace fable::platform { + +// XInput backend (xinput1_4.dll, falling back to xinput9_1_0.dll, loaded at runtime so the +// executable has no hard dependency). Polls one of the four XInput slots into retail's PadState +// convention. +class XInputPad { +public: + [[nodiscard]] static bool available() noexcept; + [[nodiscard]] static PadState poll(unsigned slot) noexcept; + // Packet number of the last poll (changes whenever the pad's state changes). + [[nodiscard]] static std::uint32_t last_packet(unsigned slot) noexcept; +}; + +} // namespace fable::platform + +#endif diff --git a/rebuild/modern/platform/src/RawMouseWin32.cpp b/rebuild/modern/platform/src/RawMouseWin32.cpp new file mode 100644 index 0000000000..eb98c18413 --- /dev/null +++ b/rebuild/modern/platform/src/RawMouseWin32.cpp @@ -0,0 +1,77 @@ +#include + +#if defined(_WIN32) + +namespace fable::platform { + +bool RawMouseWin32::register_for(HWND hwnd, bool background) noexcept +{ + RAWINPUTDEVICE device{}; + device.usUsagePage = 0x01; // generic desktop + device.usUsage = 0x02; // mouse + device.dwFlags = background ? RIDEV_INPUTSINK : 0; + device.hwndTarget = hwnd; + return RegisterRawInputDevices(&device, 1, sizeof(device)) != FALSE; +} + +bool RawMouseWin32::handle(LPARAM lparam, MouseAccumulator& into) noexcept +{ + RAWINPUT input{}; + UINT size = sizeof(input); + if (GetRawInputData(reinterpret_cast(lparam), RID_INPUT, &input, &size, sizeof(RAWINPUTHEADER)) == static_cast(-1) || + input.header.dwType != RIM_TYPEMOUSE) { + return false; + } + const RAWMOUSE& m = input.data.mouse; + if ((m.usFlags & MOUSE_MOVE_ABSOLUTE) == 0 && (m.lLastX != 0 || m.lLastY != 0)) { + into.add_motion(m.lLastX, m.lLastY); + } + const USHORT f = m.usButtonFlags; + static constexpr struct { USHORT down, up; unsigned index; } buttons[] = { + {RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_1_UP, 0}, {RI_MOUSE_BUTTON_2_DOWN, RI_MOUSE_BUTTON_2_UP, 1}, + {RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_3_UP, 2}, {RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP, 3}, + {RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP, 4}, + }; + for (const auto& b : buttons) { + if (f & b.down) { + into.button(b.index, true); + } + if (f & b.up) { + into.button(b.index, false); + } + } + if (f & RI_MOUSE_WHEEL) { + into.add_wheel(static_cast(m.usButtonData)); + } + return true; +} + +bool RawKeyboardWin32::register_for(HWND hwnd, bool background) noexcept +{ + RAWINPUTDEVICE device{}; + device.usUsagePage = 0x01; // generic desktop + device.usUsage = 0x06; // keyboard + device.dwFlags = background ? RIDEV_INPUTSINK : 0; + device.hwndTarget = hwnd; + return RegisterRawInputDevices(&device, 1, sizeof(device)) != FALSE; +} + +bool RawKeyboardWin32::handle(LPARAM lparam, KeyboardAccumulator& into) +{ + RAWINPUT input{}; + UINT size = sizeof(input); + if (GetRawInputData(reinterpret_cast(lparam), RID_INPUT, &input, &size, sizeof(RAWINPUTHEADER)) == static_cast(-1) || + input.header.dwType != RIM_TYPEKEYBOARD) { + return false; + } + const RAWKEYBOARD& k = input.data.keyboard; + if (k.VKey == 0 || k.VKey >= 255) { // 0xFF: fake keys sent as parts of escaped sequences + return true; + } + into.key(static_cast(k.VKey), (k.Flags & RI_KEY_BREAK) == 0); + return true; +} + +} // namespace fable::platform + +#endif diff --git a/rebuild/modern/platform/src/XInputPad.cpp b/rebuild/modern/platform/src/XInputPad.cpp new file mode 100644 index 0000000000..2aa327629c --- /dev/null +++ b/rebuild/modern/platform/src/XInputPad.cpp @@ -0,0 +1,86 @@ +#include + +#if defined(_WIN32) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include + +#include + +namespace fable::platform { +namespace { + +using GetStateFn = DWORD(WINAPI*)(DWORD, XINPUT_STATE*); + +GetStateFn get_state() noexcept +{ + static GetStateFn fn = [] { + for (const wchar_t* dll : {L"xinput1_4.dll", L"xinput9_1_0.dll"}) { + if (HMODULE module = LoadLibraryW(dll)) { + if (auto f = reinterpret_cast(reinterpret_cast(GetProcAddress(module, "XInputGetState")))) { + return f; + } + } + } + return GetStateFn{nullptr}; + }(); + return fn; +} + +std::uint32_t g_packets[XUSER_MAX_COUNT]{}; + +} // namespace + +bool XInputPad::available() noexcept +{ + return get_state() != nullptr; +} + +PadState XInputPad::poll(unsigned slot) noexcept +{ + PadState out; + const auto fn = get_state(); + XINPUT_STATE s{}; + if (!fn || slot >= XUSER_MAX_COUNT || fn(slot, &s) != ERROR_SUCCESS) { + return out; + } + g_packets[slot] = s.dwPacketNumber; + const XINPUT_GAMEPAD& g = s.Gamepad; + out.connected = true; + // Retail convention: +y is down (DirectInput), XInput's +y is up. + out.left_x = retail_dead_zone(stick_axis(g.sThumbLX)); + out.left_y = retail_dead_zone(-stick_axis(g.sThumbLY)); + out.right_x = stick_axis(g.sThumbRX); + out.right_y = -stick_axis(g.sThumbRY); + out.left_trigger = static_cast(g.bLeftTrigger) / 255.0f; + out.right_trigger = static_cast(g.bRightTrigger) / 255.0f; + static constexpr struct { WORD xinput; std::uint32_t pad; } map[] = { + {XINPUT_GAMEPAD_A, pad_a}, {XINPUT_GAMEPAD_B, pad_b}, {XINPUT_GAMEPAD_X, pad_x}, {XINPUT_GAMEPAD_Y, pad_y}, + {XINPUT_GAMEPAD_LEFT_SHOULDER, pad_left_shoulder}, {XINPUT_GAMEPAD_RIGHT_SHOULDER, pad_right_shoulder}, + {XINPUT_GAMEPAD_BACK, pad_back}, {XINPUT_GAMEPAD_START, pad_start}, + {XINPUT_GAMEPAD_LEFT_THUMB, pad_left_thumb}, {XINPUT_GAMEPAD_RIGHT_THUMB, pad_right_thumb}, + {XINPUT_GAMEPAD_DPAD_UP, pad_dpad_up}, {XINPUT_GAMEPAD_DPAD_DOWN, pad_dpad_down}, + {XINPUT_GAMEPAD_DPAD_LEFT, pad_dpad_left}, {XINPUT_GAMEPAD_DPAD_RIGHT, pad_dpad_right}, + }; + for (const auto& m : map) { + if (g.wButtons & m.xinput) { + out.buttons |= m.pad; + } + } + return out; +} + +std::uint32_t XInputPad::last_packet(unsigned slot) noexcept +{ + return slot < XUSER_MAX_COUNT ? g_packets[slot] : 0; +} + +} // namespace fable::platform + +#endif diff --git a/rebuild/modern/platform/tests/KeyboardAccumulatorTests.cpp b/rebuild/modern/platform/tests/KeyboardAccumulatorTests.cpp new file mode 100644 index 0000000000..e0cc53f045 --- /dev/null +++ b/rebuild/modern/platform/tests/KeyboardAccumulatorTests.cpp @@ -0,0 +1,32 @@ +#include + +#include + +int main() +{ + int failures = 0; + auto expect = [&](bool ok, const char* what) { + if (!ok) { + std::cerr << "FAIL: " << what << '\n'; + ++failures; + } + }; + fable::platform::KeyboardAccumulator kb; + kb.key('W', true); + kb.key('W', true); // auto-repeat + kb.key('E', true); + kb.key('E', false); // a tap inside one tick + auto f = kb.take(); + expect(f.held.test('W') && !f.held.test('E'), "held snapshot at the end of the tick"); + expect(f.pressed.test('W') && f.pressed.test('E'), "a tap between ticks is still pressed"); + expect(f.events.size() == 3, "auto-repeat is not an event; transitions are kept in order"); + f = kb.take(); + expect(f.held.test('W') && f.pressed.none() && f.events.empty(), "held keys carry into the next tick"); + kb.release_all(); + f = kb.take(); + expect(f.held.none() && f.events.size() == 1 && !f.events[0].down, "focus loss releases held keys"); + if (failures == 0) { + std::cout << "fable_platform.Keyboard: all tests passed\n"; + } + return failures == 0 ? 0 : 1; +} diff --git a/rebuild/modern/platform/tests/MouseAccumulatorTests.cpp b/rebuild/modern/platform/tests/MouseAccumulatorTests.cpp new file mode 100644 index 0000000000..affcb7fafe --- /dev/null +++ b/rebuild/modern/platform/tests/MouseAccumulatorTests.cpp @@ -0,0 +1,36 @@ +#include + +#include + +int main() +{ + int failures = 0; + auto expect = [&](bool ok, const char* what) { + if (!ok) { + std::cerr << "FAIL: " << what << '\n'; + ++failures; + } + }; + fable::platform::MouseAccumulator mouse; + // An 8 kHz mouse over one 15 Hz world tick: about 533 reports, far past retail's 256 buffer. + for (int i = 0; i < 2000; ++i) { + mouse.add_motion(i % 2 == 0 ? 3 : -1, 1); + } + mouse.button(0, true); + mouse.add_wheel(120); + auto frame = mouse.take(); + expect(frame.reports == 2002 + 0 && frame.dx == 2000 && frame.dy == 2000, "every report is summed, none dropped"); + expect(frame.wheel == 120 && frame.buttons_down == 1 && frame.buttons_pressed == 1, "buttons and wheel"); + frame = mouse.take(); + expect(frame.reports == 0 && frame.dx == 0 && frame.buttons_down == 1 && frame.buttons_pressed == 0, + "take() starts a fresh frame but keeps held buttons"); + mouse.button(0, false); + mouse.button(0, true); + mouse.button(0, false); + frame = mouse.take(); + expect(frame.buttons_down == 0 && frame.buttons_pressed == 1, "a click inside one tick is not lost"); + if (failures == 0) { + std::cout << "fable_platform: all tests passed\n"; + } + return failures == 0 ? 0 : 1; +} diff --git a/rebuild/modern/platform/tests/PadTests.cpp b/rebuild/modern/platform/tests/PadTests.cpp new file mode 100644 index 0000000000..80b2cc225e --- /dev/null +++ b/rebuild/modern/platform/tests/PadTests.cpp @@ -0,0 +1,38 @@ +#include + +#include +#include + +int main() +{ + int failures = 0; + auto expect = [&](bool ok, const char* what) { + if (!ok) { + std::cerr << "FAIL: " << what << '\n'; + ++failures; + } + }; + using fable::platform::retail_dead_zone; + expect(retail_dead_zone(0.19f) == 0.0f && retail_dead_zone(-0.2f) == 0.0f, "inside 20 % reads centre"); + expect(std::fabs(retail_dead_zone(0.6f) - 0.5f) < 1e-6f, "outside is rescaled: 0.6 -> 0.5"); + expect(retail_dead_zone(1.0f) == 1.0f && retail_dead_zone(-1.0f) == -1.0f, "full travel stays full"); + expect(std::fabs(retail_dead_zone(0.2001f)) < 1e-3f, "continuous at the edge"); + using fable::platform::stick_axis; + expect(stick_axis(-32768) == -1.0f && stick_axis(32767) == 1.0f && stick_axis(0) == 0.0f, "XInput range to -1..1"); + fable::platform::PadAccumulator acc; + fable::platform::PadState s; + s.connected = true; + acc.poll(s); + s.buttons = fable::platform::pad_a; + acc.poll(s); // A down... + s.buttons = 0; + acc.poll(s); // ...and up again before the tick + auto f = acc.take(); + expect(f.pressed == fable::platform::pad_a && f.state.buttons == 0, "a press between ticks is kept"); + f = acc.take(); + expect(f.pressed == 0, "pressed resets each tick"); + if (failures == 0) { + std::cout << "fable_platform.Pad: all tests passed\n"; + } + return failures == 0 ? 0 : 1; +} diff --git a/rebuild/modern/platform/tests/WorldClockTests.cpp b/rebuild/modern/platform/tests/WorldClockTests.cpp new file mode 100644 index 0000000000..0facc8a9c2 --- /dev/null +++ b/rebuild/modern/platform/tests/WorldClockTests.cpp @@ -0,0 +1,79 @@ +#include + +#include +#include +#include + +namespace { + +struct Run { + std::uint64_t world_frames = 0; + std::map renders_per_world_frame; // render count -> how many world frames had it +}; + +// Simulates the retail loop at a fixed present rate: each render-loop pass runs at most one world +// frame if due, then renders once at the next vblank. +Run simulate(double refresh_hz, double seconds) +{ + fable::platform::WorldClock clock; + Run run; + const double period = 1.0 / refresh_hz; + int renders_this_frame = 0; + const auto passes = static_cast(seconds * refresh_hz); + for (long i = 0; i < passes; ++i) { + // No accumulated float drift, and a phase offset: real vblanks do not land exactly on the + // 15 Hz grid (with exact alignment, rounding at the boundary decides 3 vs 5 renders). + const double t = (static_cast(i) + 0.3) / refresh_hz; + const double present = fable::platform::predicted_present(t, t - period, period, 0); + if (clock.due(present)) { + clock.advance(); + ++run.world_frames; + if (run.world_frames > 1) { + ++run.renders_per_world_frame[renders_this_frame]; + } + renders_this_frame = 0; + } + ++renders_this_frame; + } + return run; +} + +} // namespace + +int main() +{ + int failures = 0; + auto expect = [&](bool ok, const char* what) { + if (!ok) { + std::cerr << "FAIL: " << what << '\n'; + ++failures; + } + }; + // The live retail sample (2026-10-17): 60 Hz presents, 225 world frames in 15 s, 4 renders each. + const Run at60 = simulate(60.0, 15.0); + expect(at60.world_frames >= 224 && at60.world_frames <= 226, "15 Hz world at 60 Hz presents"); + for (const auto& [renders, frames] : at60.renders_per_world_frame) { + std::cout << "60 Hz: " << frames << " world frames with " << renders << " renders\n"; + } + expect(at60.renders_per_world_frame.size() == 1 && at60.renders_per_world_frame.count(4) == 1, + "exactly 4 renders per world frame at 60 Hz (live sample)"); + // High refresh rates change the render count, never the world rate. + for (double hz : {144.0, 165.0, 240.0, 1000.0}) { + const Run run = simulate(hz, 15.0); + expect(run.world_frames >= 224 && run.world_frames <= 226, "world stays at 15 Hz at high refresh"); + } + // A late world frame catches up one frame per loop pass, not in a burst. + fable::platform::WorldClock clock; + int ran = 0; + for (int pass = 0; pass < 3 && clock.due(1.0); ++pass) { // one second late: 14 frames due + clock.advance(); + ++ran; + } + expect(ran == 3 && clock.due(1.0), "catch-up is one frame per Update"); + fable::platform::WorldClock fresh; + expect(std::abs(fresh.interpolation(0.05) - 0.75) < 1e-9, "render fraction = present*15 - frame"); + if (failures == 0) { + std::cout << "fable_platform.WorldClock: all tests passed\n"; + } + return failures == 0 ? 0 : 1; +} diff --git a/rebuild/modern/platform/tools/fable_pad_probe.cpp b/rebuild/modern/platform/tools/fable_pad_probe.cpp new file mode 100644 index 0000000000..90f00dad92 --- /dev/null +++ b/rebuild/modern/platform/tools/fable_pad_probe.cpp @@ -0,0 +1,62 @@ +// fable_pad_probe [--seconds N] +// +// Polls the four XInput slots at about 1 kHz and takes one PadFrame per 15 Hz tick, like the game +// loop would. Prints which slots are connected, the resting stick values after retail's dead zone, +// and every tick where something changed (buttons pressed, sticks moved, triggers). + +#include +#include + +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + double seconds = 10; + for (int i = 1; i + 1 < argc; ++i) { + if (std::string(argv[i]) == "--seconds") { + seconds = std::stod(argv[++i]); + } + } + if (!fable::platform::XInputPad::available()) { + std::printf("PAD no XInput runtime\n"); + return 1; + } + fable::platform::PadAccumulator pads[4]; + for (unsigned s = 0; s < 4; ++s) { + const auto st = fable::platform::XInputPad::poll(s); + std::printf("PAD slot=%u connected=%d left=(%.3f,%.3f) right=(%.3f,%.3f) triggers=(%.2f,%.2f) buttons=0x%04x\n", s, + st.connected ? 1 : 0, st.left_x, st.left_y, st.right_x, st.right_y, st.left_trigger, st.right_trigger, + st.buttons); + } + using clock = std::chrono::steady_clock; + const auto start = clock::now(); + auto next_tick = start; + unsigned ticks = 0, active_ticks = 0; + while (std::chrono::duration(clock::now() - start).count() < seconds) { + for (unsigned s = 0; s < 4; ++s) { + pads[s].poll(fable::platform::XInputPad::poll(s)); + } + if (clock::now() >= next_tick) { + for (unsigned s = 0; s < 4; ++s) { + const auto f = pads[s].take(); + const auto& st = f.state; + const bool moved = std::fabs(st.left_x) + std::fabs(st.left_y) + std::fabs(st.right_x) + std::fabs(st.right_y) > 0.25f; + if (st.connected && (f.pressed || moved || st.left_trigger > 0.1f || st.right_trigger > 0.1f)) { + ++active_ticks; + std::printf("TICK %u slot=%u pressed=0x%04x held=0x%04x left=(%.2f,%.2f) right=(%.2f,%.2f) lt=%.2f rt=%.2f\n", ticks, + s, f.pressed, st.buttons, st.left_x, st.left_y, st.right_x, st.right_y, st.left_trigger, + st.right_trigger); + } + } + ++ticks; + next_tick += std::chrono::microseconds(66667); + } + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + std::printf("PAD done ticks=%u active_ticks=%u\n", ticks, active_ticks); + return 0; +} diff --git a/rebuild/modern/platform/tools/fable_rawmouse_probe.cpp b/rebuild/modern/platform/tools/fable_rawmouse_probe.cpp new file mode 100644 index 0000000000..17c469525f --- /dev/null +++ b/rebuild/modern/platform/tools/fable_rawmouse_probe.cpp @@ -0,0 +1,87 @@ +// fable_rawmouse_probe [--seconds N] +// +// Opens a window, registers Win32 Raw Input for the mouse (background delivery on), and takes one +// MouseFrame per 15 Hz "world tick" like the game loop does. At the end it prints the total +// reports and the largest frame: with retail's CMouseDX, a frame above 256 records would have +// overflowed (DI_BUFFEROVERFLOW); here every report must be counted. + +#include +#include + +#include +#include +#include +#include +#include + +namespace { + +fable::platform::MouseAccumulator g_mouse; +fable::platform::KeyboardAccumulator g_keyboard; + +LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM w, LPARAM l) +{ + if (msg == WM_INPUT) { + if (!fable::platform::RawMouseWin32::handle(l, g_mouse)) { + fable::platform::RawKeyboardWin32::handle(l, g_keyboard); + } + } else if (msg == WM_DESTROY) { + PostQuitMessage(0); + return 0; + } + return DefWindowProcW(hwnd, msg, w, l); +} + +} // namespace + +int main(int argc, char** argv) +{ + double seconds = 10; + for (int i = 1; i + 1 < argc; ++i) { + if (std::string(argv[i]) == "--seconds") { + seconds = std::stod(argv[++i]); + } + } + WNDCLASSW wc{}; + wc.lpfnWndProc = wnd_proc; + wc.hInstance = GetModuleHandleW(nullptr); + wc.lpszClassName = L"FableRawMouseProbe"; + RegisterClassW(&wc); + HWND hwnd = CreateWindowW(wc.lpszClassName, L"fable raw mouse probe", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 320, 200, + nullptr, nullptr, wc.hInstance, nullptr); + if (!fable::platform::RawMouseWin32::register_for(hwnd, true) || + !fable::platform::RawKeyboardWin32::register_for(hwnd, true)) { + std::cerr << "RegisterRawInputDevices failed\n"; + return 1; + } + using clock = std::chrono::steady_clock; + const auto start = clock::now(); + auto next_tick = start; + std::uint64_t total = 0, max_frame = 0, ticks = 0; + std::int64_t sum_dx = 0; + std::uint64_t key_events = 0, f24_taps = 0; + MSG msg{}; + while (std::chrono::duration(clock::now() - start).count() < seconds) { + while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + if (clock::now() >= next_tick) { // the 15 Hz world tick + const auto frame = g_mouse.take(); + total += frame.reports; + max_frame = std::max(max_frame, frame.reports); + sum_dx += frame.dx; + const auto keys = g_keyboard.take(); + key_events += keys.events.size(); + for (const auto& e : keys.events) { + f24_taps += (e.key == 0x87 && e.down) ? 1 : 0; // VK_F24, used by the injector + } + ++ticks; + next_tick += std::chrono::microseconds(66667); + } + MsgWaitForMultipleObjects(0, nullptr, FALSE, 1, QS_ALLINPUT); + } + std::cout << "RAWMOUSE ticks=" << ticks << " reports=" << total << " max_reports_per_tick=" << max_frame + << " net_dx=" << sum_dx << " key_events=" << key_events << " f24_taps=" << f24_taps << '\n'; + return 0; +} diff --git a/rebuild/modern/save/CMakeLists.txt b/rebuild/modern/save/CMakeLists.txt new file mode 100644 index 0000000000..96be8eb430 --- /dev/null +++ b/rebuild/modern/save/CMakeLists.txt @@ -0,0 +1,127 @@ +# zlib: an installed package, a local source tree (-DFABLE_ZLIB_SOURCE_DIR=...), or a pinned +# download. Use classic zlib (1.2.x/1.3.x). zlib-ng deflates differently and would break the +# byte-identical save rewrite. +set(FABLE_ZLIB_SOURCE_DIR "" CACHE PATH "Classic zlib source tree to build instead of find_package(ZLIB)") + +if(FABLE_ZLIB_SOURCE_DIR) + add_library(fable_zlib STATIC + "${FABLE_ZLIB_SOURCE_DIR}/adler32.c" + "${FABLE_ZLIB_SOURCE_DIR}/crc32.c" + "${FABLE_ZLIB_SOURCE_DIR}/deflate.c" + "${FABLE_ZLIB_SOURCE_DIR}/inffast.c" + "${FABLE_ZLIB_SOURCE_DIR}/inflate.c" + "${FABLE_ZLIB_SOURCE_DIR}/inftrees.c" + "${FABLE_ZLIB_SOURCE_DIR}/trees.c" + "${FABLE_ZLIB_SOURCE_DIR}/zutil.c" + ) + target_include_directories(fable_zlib PUBLIC "${FABLE_ZLIB_SOURCE_DIR}") + set(FABLE_ZLIB_TARGET fable_zlib) +else() + find_package(ZLIB QUIET) + if(ZLIB_FOUND) + set(FABLE_ZLIB_TARGET ZLIB::ZLIB) + else() + include(FetchContent) + FetchContent_Declare(zlib + URL https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz + URL_HASH SHA256=9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23 + ) + set(ZLIB_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(zlib) + set(FABLE_ZLIB_TARGET zlibstatic) + target_include_directories(zlibstatic PUBLIC "${zlib_SOURCE_DIR}" "${zlib_BINARY_DIR}") + endif() +endif() + +# LZO: optional, OFF by default. The QUESTS and FACTIONS text blobs are LZO1X streams from +# lzo1x_999_compress (retail 0xC08130). Upstream LZO 2.10 reproduces every game-written blob +# byte for byte, but LZO is GPL-2.0-or-later, so it is never vendored in this MIT tree: with +# the option ON it is fetched (or taken from FABLE_LZO_SOURCE_DIR) at configure time, and a +# binary built that way is a combined work under the GPL. With it OFF, text blobs still decode +# (our own decoder) and re-encode in retail's stored form, which the retail reader accepts. +option(FABLE_SAVE_WITH_LZO "Link upstream LZO 2.10 (GPL-2.0-or-later) for byte-exact QUESTS/FACTIONS re-encoding" OFF) +set(FABLE_LZO_SOURCE_DIR "" CACHE PATH "LZO 2.10 source tree to build instead of downloading it") + +if(FABLE_SAVE_WITH_LZO) + if(NOT FABLE_LZO_SOURCE_DIR) + include(FetchContent) + FetchContent_Declare(lzo + URL https://www.oberhumer.com/opensource/lzo/download/lzo-2.10.tar.gz + URL_HASH SHA256=c0f892943208266f9b6543b3ae308fab6284c5c90e627931446fb49b4221a072 + SOURCE_SUBDIR no-cmake-build + ) + FetchContent_MakeAvailable(lzo) + set(FABLE_LZO_SOURCE_DIR "${lzo_SOURCE_DIR}") + endif() + add_library(fable_lzo STATIC + "${FABLE_LZO_SOURCE_DIR}/src/lzo1x_9x.c" + "${FABLE_LZO_SOURCE_DIR}/src/lzo_init.c" + ) + target_include_directories(fable_lzo PUBLIC "${FABLE_LZO_SOURCE_DIR}/include") +endif() + +add_library(fable_save + src/Entities.cpp + src/Lzo.cpp + src/PersistStream.cpp + src/Profile.cpp + src/Regions.cpp + src/SaveBody.cpp + src/SaveContainer.cpp + src/SaveHeader.cpp + src/TextSections.cpp + src/Zlib.cpp +) + +target_include_directories(fable_save + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/include" +) +target_link_libraries(fable_save PRIVATE ${FABLE_ZLIB_TARGET} fable_codec) +if(FABLE_SAVE_WITH_LZO) + target_link_libraries(fable_save PRIVATE fable_lzo) + target_compile_definitions(fable_save PRIVATE FABLE_SAVE_HAVE_LZO=1) +endif() + +target_compile_features(fable_save PUBLIC cxx_std_23) +set_target_properties(fable_save PROPERTIES + CXX_EXTENSIONS OFF +) + +if(MSVC) + target_compile_options(fable_save PRIVATE /W4 /permissive-) +else() + target_compile_options(fable_save PRIVATE + -Wall + -Wextra + -Wconversion + -Wpedantic + ) +endif() + +add_executable(fable_save_check + tools/fable_save_check.cpp +) +target_link_libraries(fable_save_check PRIVATE fable_save) + +if(BUILD_TESTING) + add_executable(fable_save_tests + tests/SaveContainerTests.cpp + ) + target_link_libraries(fable_save_tests PRIVATE fable_save) + + if(MSVC) + target_compile_options(fable_save_tests PRIVATE /W4 /permissive-) + else() + target_compile_options(fable_save_tests PRIVATE + -Wall + -Wextra + -Wconversion + -Wpedantic + ) + endif() + + add_test(NAME fable_save.SaveContainer + COMMAND fable_save_tests + ) +endif() diff --git a/rebuild/modern/save/README.md b/rebuild/modern/save/README.md new file mode 100644 index 0000000000..ab3a1f525b --- /dev/null +++ b/rebuild/modern/save/README.md @@ -0,0 +1,123 @@ +# Save files (`.sav`, `Profile.bin`) + +Portable C++23 codec for the retail `FableSave!` container: the fork-side piece of +MODERNIZATION step 2 ("retail saves load; a save round-trips"). It decompresses and recompresses +the two persist-stream sections, enforces the retail load gates, and decodes HEADER, the body's +section list, PLAYER, ENTITIES down to the inflated SAVED_ENTITIES cells and SAVED_NPC_NAMES, +REGIONS, and the QUESTS and FACTIONS persist text (LZO). The entity streams inside the cells +stay opaque bytes. + +## Evidence boundary + +- Layout and gates: [SAVE.md](../../../docs/formats/SAVE.md) "Container and header layout" and + "Signature enforcement". The decoder accepts exactly what retail + `CUserProfileManager::VerifySignature` (0x00409730) accepts: the magic, `total_data_len + 4 <= + size`, the seed-0 `CCRC::Calc` trailer, size exactly 0x4B000, all-zero padding. It then applies the + chunk framing `CWorld::LoadGameStateInternal` (0x004A21F0) reads. +- Compression: retail links zlib **1.2.1** (`deflate 1.2.1 Copyright` in `Fable.exe`), driven by + `CDataOutputStream::WriteZlibCompressed` (0x009967E0). Level 9, 15-bit window, memLevel 8 and the + default strategy reproduce retail's compressed bytes. That holds with upstream 1.2.1 and with + classic zlib 1.3.1. **zlib-ng does not reproduce them** (Python's `zlib` here is + `1.3.1.zlib-ng`), which is why SAVE.md §2.4 originally concluded byte identity was unreachable. +- Text blobs: QUESTS and FACTIONS hold persist text compressed by `lzo1x_999_compress` + (0x00C08130, called from the blob writer 0x00996610 on all but the last 3 bytes, which are + stored raw). Upstream LZO 2.10 reproduces 8,698 of the corpus's 8,700 blobs (288 of 290 QUESTS, + all 8,410 FACTIONS records) byte for byte. The other two are QUESTS streams our Python editor + wrote in store mode. + +## Licensing (LZO) + +This tree is MIT. LZO is GPL-2.0-or-later, so it is never committed here. The decoder in +`../codec` holds the tree's own LZO1X decoder and an MIT LZO1X encoder ported from FableForge. +Without upstream LZO, the writer compresses text blobs with that encoder: a valid stream the +retail reader accepts, but not retail's exact bytes. It falls back to retail's stored form +(`i16 0` then the raw text) when compression does not help. All 8,698 corpus text blobs +re-encode with it and decode back to the same text. Configure with +`-DFABLE_SAVE_WITH_LZO=ON` to download upstream LZO 2.10 (SHA-256 pinned) or build it from +`FABLE_LZO_SOURCE_DIR`, and get byte-exact re-encoding. A binary built that way is a combined +work under the GPL, so whoever distributes it must also offer its source. Either way, everyone +gets the full source and can modify it. + +## Verified + +`fable_save_check` over every `FableSave!` file in the local save folder (409 files, 2026-10-17), +built x86 and x64: + +- 402 **exact**: the container, the typed HEADER, the body's section list, the typed PLAYER + section, ENTITIES (all 1,921 cells inflated and re-deflated at level 1, plus the NPC-name map) + and the QUESTS/FACTIONS text all decode, and every layer re-encodes byte for byte (the text + blobs only with LZO linked; without it they must decode back to the same text) (`encode(decode(file)) == file`, + all 307,200 bytes). This is every save the game itself wrote. +- 2 **sections**: valid at every layer, but recompressed by our Python tools (zlib-ng, and a + store-mode LZO stream in QUESTS), so the compressed bytes differ. +- 5 **malformed**, all backups made by our Python save editors. Three have body sections that + don't tile the stream (`body:decode_error`). Two have ENTITIES one byte short, so the next + section reads as `LAYER` (`body:unexpected_sections`). + +All 153 `Profile.bin` files (`FablePro`) also decode and re-encode byte for byte +(`profile:exact`). The same results come from MSVC x86, MSVC x64 and GCC 15 builds, with and +without LZO. + +Body shapes in the corpus: 291 full (game clock + ENTITIES, PLAYER, QUESTS, REGIONS, FACTIONS) +and 113 minimal (`.qs.hs`, ENTITIES only), matching the writer's two branches. + +`fable_save_tests` covers the CRC check value, layout, determinism, each retail gate, and the +HEADER round trip, including the minimal-context form and the tag check. It also covers a +hand-assembled LZO1X stream and the text-blob framing (empty, short, stored, and over 0x7FFF). + +## Layers + +- `SaveContainer.hpp`: `FableSave!` container, zlib sections, retail gates, `ccrc`. +- `PersistStream.hpp`: binary `CPersistContext` primitives. Each field is `[u32 field_tag(name)]` + then its value. The tag is checked the way retail `CheckCRC` asserts. Widths are pinned: + `long` → `int32_t`, `CWideString` → UTF-16 `char16_t`, UIDs → `uint64_t`, `CFloatRange`/vectors + → 3 × `float`. +- `SaveHeader.hpp`: the HEADER section (SAVE.md §3.4). +- `SaveBody.hpp`: the body. It holds the game clock (`Time`, `TimeStep`, `DayCount`) and the + named sections (`name\0`, i32 length, bytes; `CPersistContext::StartSection`/`EndSection` + 0x009BAB60/0x009BABF0), plus the typed `PlayerSection` (SAVE.md §3.5). +- `Entities.hpp`: ENTITIES (head, SAVED_ENTITIES, SAVED_NPC_NAMES) and the SAVED_ENTITIES slot + table: empty slots, cell records (zlib level 1) and the dead-entity table (SAVE.md §3.6, the + 2026-10-17 grammar). The head is the format version (2) and `CThingManager::CurrentUniqueID`. + The tail is `PersistedDeadEntities`: the UID of each killed game-persistent entity, plus the + seed-0 CRC of the quest script it belonged to. `SavedNpcNames` is `CThingManager::NPCNameMap` (UID → group, name; writer 0x00524440). + An empty group is written as `UNATTACHED`. The section is empty in the minimal `.qs.hs` form. +- `Regions.hpp`: REGIONS, the region manager's `vector` (SaveGameState 0x004FDE80). + For elements 1 onwards it stores `Name` and the `AppearOnWorldMap`, `CreatureGenerationEnabled` + and `SoundThemesEnabled` flags. Every field is `Transfer("")`, so every tag is 0. All 290 + REGIONS sections re-encode exactly. +- `TextSections.hpp`: the LZ text blob (`read_lz_text`/`write_lz_text`; its `clen` is a + signed i16, so above 0x7FFF it is -1 followed by a u32), `QuestsSection` (text plus the accepted + boasts: `NumAcceptedBoasts`, then `BoastScriptName`, `BoastId`, `BoastFailed` and + `BoastCompleted`) and `FactionsSection` (`NumberOfFactions`, then a `Faction` text each). + Every field tag in the save is now named. +- `Profile.hpp`: `Profile.bin` (`FablePro`, text-mode persist script). Entries are kept in + order with their raw values; `save_game_names()` gives the 50-slot manual-save registry. + `set_value` plus `encode` re-signs the file. Retail `VerifySignature` also has a + 0x4000-size / 0x20-pad branch for this magic, but no real profile satisfies it (all are + variable-length and unpadded). The decoder applies the CRC rule the files actually follow. + +## Build + +``` +cmake -S rebuild/modern -B build -G Ninja # find_package(ZLIB), else pinned zlib 1.3.1 +cmake -S rebuild/modern -B build -DFABLE_ZLIB_SOURCE_DIR= # offline +cmake -S rebuild/modern -B build -DFABLE_SAVE_WITH_LZO=ON # byte-exact text blobs (GPL build) +cmake --build build && ctest --test-dir build +build/save/fable_save_check "/My Games/Fable/Saves" +``` + +## Intentional modernization + +- Sections are `std::vector` and every on-disk field is an explicit little-endian + `u32`, so nothing depends on pointer width or on the retail struct images. +- Oversized output is an error (`too_large`), not a silently truncated file. + +## Not covered + +- Typed profile settings (values stay raw text; `CActionInputControl(...)` bindings unparsed). +- The entity and component records inside each cell (SAVE.md "Entity graph"). Those are the + reconstructed engine's own `Transfer` code, not a separate model. +- A typed model of the quest and faction persist text (it stays one string per blob). +- A size-0 text blob is written as a bare `u32 0`, as the writer does. No save in the corpus has + one, so how the reader handles it is unchecked. diff --git a/rebuild/modern/save/include/fable/save/Entities.hpp b/rebuild/modern/save/include/fable/save/Entities.hpp new file mode 100644 index 0000000000..681c2193c9 --- /dev/null +++ b/rebuild/modern/save/include/fable/save/Entities.hpp @@ -0,0 +1,97 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fable::save { + +// One SAVED_ENTITIES record: a map cell's persisted entities, stored zlib-compressed at level 1. +// The data is the inflated entity stream (SAVE.md "Entity graph"); it stays opaque bytes here. +struct EntityCell { + std::uint64_t cell_id = 0; + std::vector data; // empty = a record with no stream (clen 0) + std::array trailer{}; // all zero in every observed save + + friend bool operator==(const EntityCell&, const EntityCell&) = default; +}; + +// The SAVED_ENTITIES section: +// u32 0, u32 slot_count, u32 0 +// slot_count slots, each either u64 0 (empty) or +// u64 29+clen, u64 cell_id, u64 clen, u32 ulen, clen bytes zlib (level 1), 9 trailer bytes +// [tag][u32 dead_count], dead_count × { [tag][u64 uid] [tag][u32 section_name_crc] } +// The tail is CEntityRuntimePersistence::PersistedDeadEntities, filtered to the game-persistent +// entries (writer 0x521620). Every tag is field_tag("") == 0. +struct PersistedDeadEntity { + std::uint64_t uid = 0; + // Seed-0 CRC of the owning section's name: the quest script that created the entity + // (e.g. "Q_GuildTraining", "V_SickChild_Activate"), or "NULL" (0x321ACEE7) for none. + std::uint32_t section_name_crc = 0; + + friend bool operator==(const PersistedDeadEntity&, const PersistedDeadEntity&) = default; +}; + +struct SavedEntities { + std::vector> slots; + std::vector dead_entities; + + friend bool operator==(const SavedEntities&, const SavedEntities&) = default; + + [[nodiscard]] static std::expected decode(std::span section); + [[nodiscard]] std::expected, PersistError> encode() const; +}; + +// SAVED_NPC_NAMES: CThingManager::NPCNameMap, map>, written +// by 0x524440 in key order. Binary saves write u32 count, then per entry u64 uid, the group as a +// counted string and the name as NUL-terminated UTF-16. An empty group is written as "UNATTACHED" +// and read back as empty (loader 0x525A62), so `group` holds the in-memory value. A context +// with the +0x102 flag set (the minimal no-clock `.qs.hs` form) writes an empty section +// (`entries` = nullopt). +struct NpcName { + std::uint64_t uid = 0; + std::string group; + std::u16string name; + + friend bool operator==(const NpcName&, const NpcName&) = default; +}; + +struct SavedNpcNames { + std::optional> entries; + + friend bool operator==(const SavedNpcNames&, const SavedNpcNames&) = default; + + [[nodiscard]] static std::expected decode(std::span section); + [[nodiscard]] std::vector encode() const; +}; + +// The ENTITIES head of a full save (writer 0x524390): [tag][u32 format version] [tag][u64 +// CThingManager::CurrentUniqueID], the next free global UID. Every tag is field_tag("") == 0. +// The minimal `.qs.hs` form has no head. +struct EntitiesHead { + std::uint32_t format_version = 2; // 2 in every observed save + std::uint64_t current_unique_id = 0; + + friend bool operator==(const EntitiesHead&, const EntitiesHead&) = default; +}; + +// The ENTITIES section: the optional head, then the SAVED_ENTITIES and SAVED_NPC_NAMES sections. +struct EntitiesSection { + std::optional head; + SavedEntities saved_entities; + SavedNpcNames saved_npc_names; + + friend bool operator==(const EntitiesSection&, const EntitiesSection&) = default; + + [[nodiscard]] static std::expected decode(std::span section); + [[nodiscard]] std::expected, PersistError> encode() const; +}; + +} // namespace fable::save diff --git a/rebuild/modern/save/include/fable/save/PersistStream.hpp b/rebuild/modern/save/include/fable/save/PersistStream.hpp new file mode 100644 index 0000000000..d11b93a5ac --- /dev/null +++ b/rebuild/modern/save/include/fable/save/PersistStream.hpp @@ -0,0 +1,83 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fable::save { + +enum class PersistError { + truncated, // the stream ends inside a tag or value + tag_mismatch, // retail CheckCRC asserts stream_crc == CCharString(name).GetCRC() + invalid_bool, // a bool byte other than 0 or 1 + unterminated, // a string runs to the end of the stream without its NUL + bad_section, // section name or length framing does not match +}; + +// The on-disk tag of a field: seed-0 CCRC::Calc over the field name's bytes, no terminator. +[[nodiscard]] std::uint32_t field_tag(std::string_view name) noexcept; + +// Reader for the binary CPersistContext stream (mode 2). Every Transfer(name, value) is +// `[u32 field_tag(name)][value]`, and value widths are fixed by T, not by the host: long and +// unsigned long are 4 bytes, CWideString is UTF-16LE, UIDs are 8 bytes (docs/modern/PERSISTED_TYPES.md). +class PersistReader final { +public: + explicit PersistReader(std::span bytes) noexcept : bytes_(bytes) {} + + [[nodiscard]] std::size_t position() const noexcept { return pos_; } + [[nodiscard]] std::size_t remaining() const noexcept { return bytes_.size() - pos_; } + + [[nodiscard]] std::expected tag(std::string_view name); + + [[nodiscard]] std::expected u32(); + [[nodiscard]] std::expected i32(); + [[nodiscard]] std::expected u64(); + [[nodiscard]] std::expected f32(); + [[nodiscard]] std::expected boolean(); + [[nodiscard]] std::expected cstring(); // CCharString: bytes + NUL + [[nodiscard]] std::expected wstring(); // CWideString: UTF-16LE + NUL + [[nodiscard]] std::expected counted_string(); // u32 length, then bytes (0x995A80) + [[nodiscard]] std::expected, PersistError> vector3(); + [[nodiscard]] std::expected, PersistError> raw(std::size_t count); + + // `name\0` then u32 length; returns the section's bytes and advances past them. + [[nodiscard]] std::expected, PersistError> section(std::string_view name); + +private: + [[nodiscard]] std::expected, PersistError> take(std::size_t count); + + std::span bytes_; + std::size_t pos_ = 0; +}; + +// Writer for the binary CPersistContext stream (mode 3); the exact inverse of PersistReader. +class PersistWriter final { +public: + void tag(std::string_view name); + void u32(std::uint32_t value); + void i32(std::int32_t value); + void u64(std::uint64_t value); + void f32(float value); + void boolean(bool value); + void cstring(std::string_view value); + void wstring(std::u16string_view value); + void counted_string(std::string_view value); + void vector3(const std::array& value); + + // Wraps `body` as a named section: `name\0`, u32 body length, body. + void section(std::string_view name, std::span body); + void bytes_raw(std::span raw); + + [[nodiscard]] const std::vector& bytes() const noexcept { return bytes_; } + [[nodiscard]] std::vector take() noexcept { return std::move(bytes_); } + +private: + std::vector bytes_; +}; + +} // namespace fable::save diff --git a/rebuild/modern/save/include/fable/save/Profile.hpp b/rebuild/modern/save/include/fable/save/Profile.hpp new file mode 100644 index 0000000000..71e8c0c7a2 --- /dev/null +++ b/rebuild/modern/save/include/fable/save/Profile.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace fable::save { + +enum class ProfileError { + bad_magic, // not "FablePro" + u32 0 + truncated, + data_length_out_of_range, // total_data_len must equal file size - 4 + bad_signature, // trailer != ccrc(file[0 : total_data_len]) + bad_line, // a line that is not `Name value;` + CRLF +}; + +// `Profile.bin`: the `FablePro` container around a text-mode CPersistContext script +// (docs/formats/SAVE.md "Profile index"): +// 0x00 "FablePro", u32 0, u32 total_data_len (= file size - 4), latin1 text, u32 ccrc trailer. +// The text is one `Name value;\r\n` line per field, in writer order, so entries keep their order +// and raw value spelling and re-encode byte for byte. +// +// Retail VerifySignature (0x00409730) also has a 0x4000-size / 0x20-padding branch for this magic, +// but every real profile is variable-length and unpadded (153/153 here). The decoder applies the +// CRC rule the files actually satisfy. +class Profile final { +public: + struct Entry { + std::string name; // e.g. "SaveGameNames1", "AssignedInputsPassive[3]" + std::string value; // raw text between the space and the ';', e.g. "\"Manual - Save1\"" + + friend bool operator==(const Entry&, const Entry&) = default; + }; + + static constexpr std::size_t save_slot_count = 50; + + [[nodiscard]] static std::expected decode(std::span file); + [[nodiscard]] std::vector encode() const; + + [[nodiscard]] const std::vector& entries() const noexcept { return entries_; } + [[nodiscard]] std::optional value(std::string_view name) const noexcept; + // Replaces an existing entry's raw value; returns false if there is no such entry. + bool set_value(std::string_view name, std::string value); + + // The manual-save registry `SaveGameNames1..50`, unquoted. An empty string is a free slot. + [[nodiscard]] std::vector save_game_names() const; + + friend bool operator==(const Profile&, const Profile&) = default; + +private: + std::vector entries_; +}; + +} // namespace fable::save diff --git a/rebuild/modern/save/include/fable/save/Regions.hpp b/rebuild/modern/save/include/fable/save/Regions.hpp new file mode 100644 index 0000000000..079bf0a0a1 --- /dev/null +++ b/rebuild/modern/save/include/fable/save/Regions.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace fable::save { + +// One CRegion's saved state (the region manager's vector, 0x58-byte elements in retail). +struct Region { + std::string name; // CRegion::Name + bool appear_on_world_map = false; // +0x54 + bool creature_generation_enabled = false; // +0x55 + bool sound_themes_enabled = false; // +0x56 + + friend bool operator==(const Region&, const Region&) = default; +}; + +// REGIONS (SaveGameState 0x4FDE80, per region 0x6BC290). Every field is Transfer("", ...), so +// every tag is field_tag("") == 0: +// [tag][u32 table size] +// (table size - 1) × { [tag][name\0] [tag][bool] [tag][bool] [tag][bool] } +// Element 0 of the engine's table is never written; `regions` holds elements 1 onwards, so the +// stored table size is regions.size() + 1. +struct RegionsSection { + std::vector regions; + + friend bool operator==(const RegionsSection&, const RegionsSection&) = default; + + [[nodiscard]] static std::expected decode(std::span section); + [[nodiscard]] std::vector encode() const; +}; + +} // namespace fable::save diff --git a/rebuild/modern/save/include/fable/save/SaveBody.hpp b/rebuild/modern/save/include/fable/save/SaveBody.hpp new file mode 100644 index 0000000000..c95c281fcc --- /dev/null +++ b/rebuild/modern/save/include/fable/save/SaveBody.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace fable::save { + +// The body section, chunk1 of a `.sav`, as CWorld::SaveGameStateInternal (0x0049F4C0) writes it: +// +// [game clock: Time f32, TimeStep f32, DayCount i32] (written by 0x006BBB30) +// ENTITIES section (CThingManager, via 0x00524290) +// PLAYER section (CPlayerManager::SaveGameState 0x00449F90) +// QUESTS section (0x004B64E0) +// REGIONS section (0x004FDE80) +// FACTIONS section (0x006B9560) +// +// In the writer's minimal-context branch (`this+0x102 != 0`, the `.qs.hs` files) only ENTITIES +// is written. Each section is `name\0`, an i32 length patched by EndSection (0x009BABF0), then its +// bytes. Section contents other than PLAYER stay opaque bytes at this layer. +struct SaveBody { + struct GameClock { + float time = 0.0F; + float time_step = 0.0F; + std::int32_t day_count = 0; + + friend bool operator==(const GameClock&, const GameClock&) = default; + }; + + struct Section { + std::string name; + std::vector bytes; + + friend bool operator==(const Section&, const Section&) = default; + }; + + std::optional clock; + std::vector
sections; + + friend bool operator==(const SaveBody&, const SaveBody&) = default; + + // Requires the sections to tile the stream exactly. + [[nodiscard]] static std::expected decode(std::span body); + [[nodiscard]] std::vector encode() const; + + [[nodiscard]] const Section* find(std::string_view name) const noexcept; + + // True for the two shapes the retail writer produces: clock + ENTITIES, PLAYER, QUESTS, + // REGIONS, FACTIONS, or no clock + ENTITIES alone (minimal context). + [[nodiscard]] bool has_retail_shape() const noexcept; +}; + +// The PLAYER section (SAVE.md §3.5): exactly two tagged fields. +struct PlayerSection { + std::uint64_t player_character_uid = 0; // UID of the hero CThing in SAVED_ENTITIES + std::string player_region_name; // e.g. "Greatwood" + + friend bool operator==(const PlayerSection&, const PlayerSection&) = default; + + [[nodiscard]] static std::expected decode(std::span section); + [[nodiscard]] std::vector encode() const; +}; + +} // namespace fable::save diff --git a/rebuild/modern/save/include/fable/save/SaveContainer.hpp b/rebuild/modern/save/include/fable/save/SaveContainer.hpp new file mode 100644 index 0000000000..9b1fc2f815 --- /dev/null +++ b/rebuild/modern/save/include/fable/save/SaveContainer.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace fable::save { + +// Retail CCRC::Calc: reflected CRC-32 (poly 0xEDB88320, the zlib table), caller seed, no final +// inversion. Every on-disk hash in Fable uses seed 0 (save trailer, field tags, def tags). +[[nodiscard]] std::uint32_t ccrc(std::span data, std::uint32_t seed = 0) noexcept; + +enum class SaveContainerError { + bad_magic, // not "FableSave!" + 00 00 + truncated_header, // shorter than the 0x1C-byte header + data_length_out_of_range, // total_data_len + 4 > file size + bad_signature, // trailer != ccrc(file[0 : total_data_len]) + bad_slab_size, // retail requires exactly 0x4B000 bytes + nonzero_padding, // bytes after the trailer must all be 0x00 + chunk_out_of_bounds, // a chunk length runs past total_data_len + chunk_layout_mismatch, // chunk1 does not end exactly at total_data_len + inflate_failed, + inflated_size_mismatch, // a section's size differs from its header field + deflate_failed, + too_large, // the rewritten data does not fit the slab +}; + +// The two persist-stream sections of a retail `.sav` (`FableSave!` container), decompressed. +// +// On disk (docs/formats/SAVE.md "Container and header layout"): +// 0x00 "FableSave!" 00 00 +// 0x0C u32 total_data_len (= trailer offset) +// 0x10 u32 header_size (inflated HEADER section) +// 0x14 u32 body_size (inflated main section) +// 0x18 u32 header_zlib_size +// 0x1C header zlib stream, u32 body_zlib_size, body zlib stream +// total_data_len: u32 ccrc(file[0 : total_data_len]), then zeros to 0x4B000. +// +// All fields are explicit little-endian 32-bit values, so the layout is the same on every pointer +// width; nothing here depends on the retail 32-bit struct images. +struct SaveContainer { + std::vector header_section; + std::vector body_section; + + friend bool operator==(const SaveContainer&, const SaveContainer&) = default; +}; + +class SaveContainerCodec final { +public: + static constexpr std::size_t slab_size = 0x4B000; + static constexpr std::size_t header_size = 0x1C; + + // Accepts exactly what retail CUserProfileManager::VerifySignature (0x00409730) accepts, plus + // the chunk framing CWorld::LoadGameStateInternal (0x004A21F0) then reads. + [[nodiscard]] static std::expected + decode(std::span file); + + // Deflates each section the way retail CDataOutputStream::WriteZlibCompressed (0x009967E0) + // does: zlib level 9, 15-bit window, memLevel 8, default strategy. With classic zlib (1.2.1 + // through 1.3.x; not zlib-ng) this reproduces retail-written saves byte for byte. + [[nodiscard]] static std::expected, SaveContainerError> + encode(const SaveContainer& container); +}; + +} // namespace fable::save diff --git a/rebuild/modern/save/include/fable/save/SaveHeader.hpp b/rebuild/modern/save/include/fable/save/SaveHeader.hpp new file mode 100644 index 0000000000..ff20dd1e27 --- /dev/null +++ b/rebuild/modern/save/include/fable/save/SaveHeader.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fable::save { + +// The HEADER section, chunk0 of a `.sav`. Written by CWorld::SaveGameStateInternal (0x0049F4C0); +// field order is its Transfer call order (docs/formats/SAVE.md §3.4, 23/23 fields confirmed). +struct SaveHeader { + // Fields 2-22. The writer skips them in its minimal-context branch (`this+0x102 != 0`). + struct Details { + bool teleporting_enabled = false; + bool saving_enabled = false; + bool experience_spending_enabled = false; + bool creature_generation_enabled = false; + std::int32_t creature_generation_disabled_groups = 0; // bitmask + bool hero_sleeping_enabled = false; + bool map_table_show_quest_cards_on_used = false; + bool mini_map_enabled = false; + bool mini_map_active_before_disabled = false; + bool guild_master_messages_enabled = false; + bool summoner_death_explosion_affects_hero = false; + std::int32_t most_recent_save_type = 0; // CWorld::ESaveType + std::int32_t most_recent_save_type_before_manual_save = 0; + std::u16string most_recent_manual_save_name; // empty in autosaves + std::array save_game_marker_pos{}; + float save_game_marker_angle_xy = 0.0F; + std::array guild_seal_recall_pos{}; + float guild_seal_recall_angle_xy = 0.0F; + std::string current_region_name; // e.g. TXT_REGION_OAKVALE + std::string current_region_minimap_graphic_name; // e.g. MINIMAP_GREATWOOD + float total_time_played = 0.0F; // 4 bytes on disk + + friend bool operator==(const Details&, const Details&) = default; + }; + + std::string world_name; // "FinalAlbion" + std::int32_t world_frame = 0; // world frames (15 per second) at save time + std::optional
details; + + friend bool operator==(const SaveHeader&, const SaveHeader&) = default; + + // `section` is the whole chunk0 stream: "HEADER\0", u32 length, tagged fields. + [[nodiscard]] static std::expected decode(std::span section); + [[nodiscard]] std::vector encode() const; +}; + +} // namespace fable::save diff --git a/rebuild/modern/save/include/fable/save/TextSections.hpp b/rebuild/modern/save/include/fable/save/TextSections.hpp new file mode 100644 index 0000000000..5aaf64ac47 --- /dev/null +++ b/rebuild/modern/save/include/fable/save/TextSections.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace fable::save { + +// A compressed persist-text blob, as QUESTS and FACTIONS store them: +// u32 size +// size == 0: nothing more +// otherwise: i16 clen; clen == -1: u32 clen follows +// clen <= 0: `size` raw bytes (retail's stored form) +// clen > 0: clen bytes LZO1X decoding to the first size - 3 bytes, then the +// last 3 bytes raw +// Writer 0x996610 / reader 0x995FE0. The reader takes clen as signed, so the writer stores at +// most 0x7FFF in 16 bits and uses -1 + u32 above that. It writes the stored form for size <= 3 +// and when the compressed stream would not be shorter than size - 3. +[[nodiscard]] std::expected read_lz_text(PersistReader& in); + +// Byte-exact with retail when the build links upstream LZO (lzo_compressor_available()); +// otherwise compressed with the tree's MIT LZO1X encoder: a valid stream that decodes to the same +// text, but not retail's exact bytes. Falls back to the stored form if that is not smaller. +void write_lz_text(PersistWriter& out, std::string_view text); + +[[nodiscard]] bool lzo_compressor_available() noexcept; + +// One accepted boast, as the QUESTS section's tagged tail stores it. +struct AcceptedBoast { + std::string script_name; // BoastScriptName: the quest the boast belongs to + std::uint32_t id = 0; // BoastId + bool failed = false; // BoastFailed + bool completed = false; // BoastCompleted + + friend bool operator==(const AcceptedBoast&, const AcceptedBoast&) = default; +}; + +// QUESTS: the quest manager's persist text (START_QUEST_CARDS ... END_SAVED_QUESTS), then the +// accepted boasts as tagged binary fields: NumAcceptedBoasts, then per boast i the fields +// BoastScriptName, BoastId, BoastFailed, BoastCompleted (the index is part of the +// field name, so of its tag). +struct QuestsSection { + std::string text; + std::vector accepted_boasts; + + friend bool operator==(const QuestsSection&, const QuestsSection&) = default; + + [[nodiscard]] static std::expected decode(std::span section); + [[nodiscard]] std::vector encode() const; +}; + +// FACTIONS: tagged NumberOfFactions (u32), then per faction i the tagged Faction LZ text +// blob. Each text is one faction's `Name "FACTION_X"; NumberOfAllies n; Ally "..."; ... +// EndFaction`. The tags follow from the names, so only the texts are kept. +struct FactionsSection { + std::vector factions; + + friend bool operator==(const FactionsSection&, const FactionsSection&) = default; + + [[nodiscard]] static std::expected decode(std::span section); + [[nodiscard]] std::vector encode() const; +}; + +} // namespace fable::save diff --git a/rebuild/modern/save/src/Entities.cpp b/rebuild/modern/save/src/Entities.cpp new file mode 100644 index 0000000000..bbcf47f7f5 --- /dev/null +++ b/rebuild/modern/save/src/Entities.cpp @@ -0,0 +1,261 @@ +#include + +#include "Zlib.hpp" + +#include +#include + +namespace fable::save { +namespace { + +constexpr std::uint64_t record_overhead = 29; // cell_id 8 + clen 8 + ulen 4 + trailer 9 +constexpr int cell_zlib_level = 1; +constexpr std::string_view unattached_group = "UNATTACHED"; + +// The position of the first `name\0` in `bytes`, as a section start. +std::optional find_section(std::span bytes, std::string_view name) +{ + const auto it = std::search(bytes.begin(), bytes.end(), name.begin(), name.end(), + [](std::byte b, char c) { return b == static_cast(c); }); + if (it == bytes.end()) { + return std::nullopt; + } + const auto at = static_cast(it - bytes.begin()); + if (at + name.size() >= bytes.size() || bytes[at + name.size()] != std::byte{0}) { + return std::nullopt; + } + return at; +} + +} // namespace + +std::expected SavedEntities::decode(std::span section) +{ + PersistReader in(section); + const auto lead = in.u32(); + const auto slot_count = in.u32(); + const auto pad = in.u32(); + if (!lead || !slot_count || !pad) { + return std::unexpected(PersistError::truncated); + } + if (*lead != 0 || *pad != 0 || *slot_count > in.remaining() / 8) { + return std::unexpected(PersistError::bad_section); + } + + SavedEntities out; + out.slots.reserve(*slot_count); + for (std::uint32_t i = 0; i < *slot_count; ++i) { + const auto record_length = in.u64(); + if (!record_length) { + return std::unexpected(record_length.error()); + } + if (*record_length == 0) { + out.slots.emplace_back(std::nullopt); + continue; + } + const auto cell_id = in.u64(); + const auto clen = in.u64(); + const auto ulen = in.u32(); + if (!cell_id || !clen || !ulen) { + return std::unexpected(PersistError::truncated); + } + if (*record_length != record_overhead + *clen || *clen > in.remaining()) { + return std::unexpected(PersistError::bad_section); + } + const auto compressed = in.raw(static_cast(*clen)); + const auto trailer = in.raw(9); + if (!compressed || !trailer) { + return std::unexpected(PersistError::truncated); + } + EntityCell cell; + cell.cell_id = *cell_id; + std::copy(trailer->begin(), trailer->end(), cell.trailer.begin()); + if (*clen != 0) { + auto data = detail::inflate_exact(*compressed, *ulen); + if (!data) { + return std::unexpected(PersistError::bad_section); + } + cell.data = std::move(*data); + } else if (*ulen != 0) { + return std::unexpected(PersistError::bad_section); + } + out.slots.emplace_back(std::move(cell)); + } + + const auto dead_count = in.u32(); + if (!dead_count) { + return std::unexpected(dead_count.error()); + } + if (in.remaining() != static_cast(*dead_count) * 20) { + return std::unexpected(PersistError::bad_section); + } + out.dead_entities.resize(*dead_count); + for (PersistedDeadEntity& dead : out.dead_entities) { + if (const auto tag = in.tag(""); !tag) { + return std::unexpected(tag.error()); + } + const auto uid = in.u64(); + if (const auto tag = in.tag(""); !tag) { + return std::unexpected(tag.error()); + } + const auto crc = in.u32(); + dead = PersistedDeadEntity{*uid, *crc}; // the size check above covers both reads + } + return out; +} + +std::expected, PersistError> SavedEntities::encode() const +{ + PersistWriter out; + out.u32(0); + out.u32(static_cast(slots.size())); + out.u32(0); + for (const auto& slot : slots) { + if (!slot) { + out.u64(0); + continue; + } + std::vector compressed; + if (!slot->data.empty()) { + auto deflated = detail::deflate_retail(slot->data, cell_zlib_level); + if (!deflated) { + return std::unexpected(PersistError::bad_section); + } + compressed = std::move(*deflated); + } + out.u64(record_overhead + compressed.size()); + out.u64(slot->cell_id); + out.u64(compressed.size()); + out.u32(static_cast(slot->data.size())); + out.bytes_raw(compressed); + out.bytes_raw(slot->trailer); + } + out.u32(static_cast(dead_entities.size())); + for (const PersistedDeadEntity& dead : dead_entities) { + out.tag(""); + out.u64(dead.uid); + out.tag(""); + out.u32(dead.section_name_crc); + } + return out.take(); +} + +std::expected SavedNpcNames::decode(std::span section) +{ + SavedNpcNames out; + if (section.empty()) { + return out; + } + PersistReader in(section); + const auto count = in.u32(); + if (!count) { + return std::unexpected(count.error()); + } + // Each entry is at least u64 + u32 + a UTF-16 NUL. + if (*count > in.remaining() / 14) { + return std::unexpected(PersistError::bad_section); + } + out.entries.emplace(); + out.entries->reserve(*count); + for (std::uint32_t i = 0; i < *count; ++i) { + const auto uid = in.u64(); + if (!uid) { + return std::unexpected(uid.error()); + } + auto group = in.counted_string(); + if (!group) { + return std::unexpected(group.error()); + } + auto name = in.wstring(); + if (!name) { + return std::unexpected(name.error()); + } + if (*group == unattached_group) { + group->clear(); + } + out.entries->push_back(NpcName{*uid, std::move(*group), std::move(*name)}); + } + if (in.remaining() != 0) { + return std::unexpected(PersistError::bad_section); + } + return out; +} + +std::vector SavedNpcNames::encode() const +{ + PersistWriter out; + if (!entries) { + return out.take(); + } + out.u32(static_cast(entries->size())); + for (const NpcName& entry : *entries) { + out.u64(entry.uid); + out.counted_string(entry.group.empty() ? unattached_group : std::string_view(entry.group)); + out.wstring(entry.name); + } + return out.take(); +} + +std::expected EntitiesSection::decode(std::span section) +{ + const auto at = find_section(section, "SAVED_ENTITIES"); + if (!at) { + return std::unexpected(PersistError::bad_section); + } + EntitiesSection out; + if (*at != 0) { + PersistReader head(section.first(*at)); + const auto version_tag = head.tag(""); + const auto version = head.u32(); + const auto uid_tag = head.tag(""); + const auto uid = head.u64(); + if (!version_tag || !version || !uid_tag || !uid || head.remaining() != 0) { + return std::unexpected(PersistError::bad_section); + } + out.head = EntitiesHead{*version, *uid}; + } + + PersistReader in(section.subspan(*at)); + const auto saved = in.section("SAVED_ENTITIES"); + if (!saved) { + return std::unexpected(saved.error()); + } + auto entities = SavedEntities::decode(*saved); + if (!entities) { + return std::unexpected(entities.error()); + } + out.saved_entities = std::move(*entities); + const auto names = in.section("SAVED_NPC_NAMES"); + if (!names) { + return std::unexpected(names.error()); + } + auto npc_names = SavedNpcNames::decode(*names); + if (!npc_names) { + return std::unexpected(npc_names.error()); + } + out.saved_npc_names = std::move(*npc_names); + if (in.remaining() != 0) { + return std::unexpected(PersistError::bad_section); + } + return out; +} + +std::expected, PersistError> EntitiesSection::encode() const +{ + auto saved = saved_entities.encode(); + if (!saved) { + return std::unexpected(saved.error()); + } + PersistWriter out; + if (head) { + out.tag(""); + out.u32(head->format_version); + out.tag(""); + out.u64(head->current_unique_id); + } + out.section("SAVED_ENTITIES", *saved); + out.section("SAVED_NPC_NAMES", saved_npc_names.encode()); + return out.take(); +} + +} // namespace fable::save diff --git a/rebuild/modern/save/src/Lzo.cpp b/rebuild/modern/save/src/Lzo.cpp new file mode 100644 index 0000000000..ebeba135f5 --- /dev/null +++ b/rebuild/modern/save/src/Lzo.cpp @@ -0,0 +1,53 @@ +#include "Lzo.hpp" + +#include + +#include + +#if defined(FABLE_SAVE_HAVE_LZO) +#include +#endif + +namespace fable::save::detail { + +std::expected, LzoError> lzo1x_decompress(std::span stream, + std::size_t expected_size) +{ + auto out = fable::codec::lzo1x_decompress(stream, expected_size); + if (!out) { + return std::unexpected(static_cast(out.error())); // the two enums share their order + } + return std::move(*out); +} + +bool lzo_compressor_available() noexcept +{ +#if defined(FABLE_SAVE_HAVE_LZO) + return true; +#else + return false; +#endif +} + +std::optional> lzo1x_999_compress(std::span data) +{ +#if defined(FABLE_SAVE_HAVE_LZO) + // lzo1x_999 never expands by more than size / 16 + 64 + 3 (LZO's documented bound). + std::vector out(data.size() + data.size() / 16 + 64 + 3); + std::vector work(LZO1X_999_MEM_COMPRESS); + lzo_uint out_size = 0; + const int status = ::lzo1x_999_compress(reinterpret_cast(data.data()), + static_cast(data.size()), + reinterpret_cast(out.data()), &out_size, work.data()); + if (status != LZO_E_OK) { + return std::nullopt; + } + out.resize(out_size); + return out; +#else + (void)data; + return std::nullopt; +#endif +} + +} // namespace fable::save::detail diff --git a/rebuild/modern/save/src/Lzo.hpp b/rebuild/modern/save/src/Lzo.hpp new file mode 100644 index 0000000000..8f4f5d1572 --- /dev/null +++ b/rebuild/modern/save/src/Lzo.hpp @@ -0,0 +1,33 @@ +#pragma once + +// Internal: the LZO1X operations the QUESTS and FACTIONS text blobs need. +// +// The decoder is this module's own implementation of the LZO1X stream format (MIT, like the +// rest of the tree). The compressor is upstream LZO's lzo1x_999_compress, which retail calls +// through 0xC08130 (level-8 parameters, 0x70000 bytes of work memory). LZO is GPL-2.0-or-later, +// so it is linked only when the build defines FABLE_SAVE_HAVE_LZO (CMake FABLE_SAVE_WITH_LZO). + +#include +#include +#include +#include +#include + +namespace fable::save::detail { + +enum class LzoError { + truncated, // the stream ends inside a token, a literal run or a match + bad_distance, // a match reaches back before the start of the output + size_mismatch, // the stream decodes to a different size than expected + trailing_bytes, // bytes follow the end-of-stream marker +}; + +[[nodiscard]] std::expected, LzoError> lzo1x_decompress( + std::span stream, std::size_t expected_size); + +[[nodiscard]] bool lzo_compressor_available() noexcept; + +// lzo1x_999_compress(data); nullopt when the build has no LZO or compression fails. +[[nodiscard]] std::optional> lzo1x_999_compress(std::span data); + +} // namespace fable::save::detail diff --git a/rebuild/modern/save/src/PersistStream.cpp b/rebuild/modern/save/src/PersistStream.cpp new file mode 100644 index 0000000000..a0aaa073d5 --- /dev/null +++ b/rebuild/modern/save/src/PersistStream.cpp @@ -0,0 +1,241 @@ +#include + +#include + +#include + +namespace fable::save { +namespace { + +std::uint32_t le32(std::span b) noexcept +{ + return static_cast(b[0]) | static_cast(b[1]) << 8 + | static_cast(b[2]) << 16 | static_cast(b[3]) << 24; +} + +std::span as_bytes(std::string_view text) noexcept +{ + return {reinterpret_cast(text.data()), text.size()}; +} + +} // namespace + +std::uint32_t field_tag(std::string_view name) noexcept +{ + return ccrc(as_bytes(name)); +} + +std::expected, PersistError> PersistReader::take(std::size_t count) +{ + if (count > remaining()) { + return std::unexpected(PersistError::truncated); + } + const auto out = bytes_.subspan(pos_, count); + pos_ += count; + return out; +} + +std::expected PersistReader::tag(std::string_view name) +{ + const auto value = u32(); + if (!value) { + return std::unexpected(value.error()); + } + if (*value != field_tag(name)) { + return std::unexpected(PersistError::tag_mismatch); + } + return {}; +} + +std::expected PersistReader::u32() +{ + return take(4).transform(le32); +} + +std::expected PersistReader::i32() +{ + return u32().transform([](std::uint32_t v) { return static_cast(v); }); +} + +std::expected PersistReader::u64() +{ + return take(8).transform([](std::span b) { + return static_cast(le32(b.first(4))) | static_cast(le32(b.subspan(4))) << 32; + }); +} + +std::expected PersistReader::f32() +{ + return u32().transform([](std::uint32_t v) { return std::bit_cast(v); }); +} + +std::expected PersistReader::boolean() +{ + const auto b = take(1); + if (!b) { + return std::unexpected(b.error()); + } + const auto v = std::to_integer((*b)[0]); + if (v > 1) { + return std::unexpected(PersistError::invalid_bool); + } + return v == 1; +} + +std::expected PersistReader::cstring() +{ + std::string out; + for (std::size_t i = pos_; i < bytes_.size(); ++i) { + if (bytes_[i] == std::byte{0}) { + pos_ = i + 1; + return out; + } + out.push_back(static_cast(bytes_[i])); + } + return std::unexpected(PersistError::unterminated); +} + +std::expected PersistReader::counted_string() +{ + const auto length = u32(); + if (!length) { + return std::unexpected(length.error()); + } + const auto raw = take(*length); + if (!raw) { + return std::unexpected(raw.error()); + } + std::string out; + out.reserve(raw->size()); + for (const std::byte b : *raw) { + out.push_back(static_cast(b)); + } + return out; +} + +std::expected PersistReader::wstring() +{ + std::u16string out; + for (std::size_t i = pos_; i + 1 < bytes_.size(); i += 2) { + const auto unit = static_cast(std::to_integer(bytes_[i]) + | std::to_integer(bytes_[i + 1]) << 8); + if (unit == 0) { + pos_ = i + 2; + return out; + } + out.push_back(unit); + } + return std::unexpected(PersistError::unterminated); +} + +std::expected, PersistError> PersistReader::vector3() +{ + std::array out{}; + for (float& v : out) { + const auto f = f32(); + if (!f) { + return std::unexpected(f.error()); + } + v = *f; + } + return out; +} + +std::expected, PersistError> PersistReader::raw(std::size_t count) +{ + return take(count); +} + +std::expected, PersistError> PersistReader::section(std::string_view name) +{ + const auto got = cstring(); + if (!got) { + return std::unexpected(got.error()); + } + if (*got != name) { + return std::unexpected(PersistError::bad_section); + } + const auto length = u32(); + if (!length) { + return std::unexpected(length.error()); + } + return take(*length); +} + +void PersistWriter::tag(std::string_view name) +{ + u32(field_tag(name)); +} + +void PersistWriter::u32(std::uint32_t value) +{ + for (int i = 0; i < 4; ++i) { + bytes_.push_back(static_cast((value >> (8 * i)) & 0xFFU)); + } +} + +void PersistWriter::i32(std::int32_t value) +{ + u32(static_cast(value)); +} + +void PersistWriter::u64(std::uint64_t value) +{ + u32(static_cast(value & 0xFFFFFFFFU)); + u32(static_cast(value >> 32)); +} + +void PersistWriter::f32(float value) +{ + u32(std::bit_cast(value)); +} + +void PersistWriter::boolean(bool value) +{ + bytes_.push_back(value ? std::byte{1} : std::byte{0}); +} + +void PersistWriter::cstring(std::string_view value) +{ + const auto raw = as_bytes(value); + bytes_.insert(bytes_.end(), raw.begin(), raw.end()); + bytes_.push_back(std::byte{0}); +} + +void PersistWriter::counted_string(std::string_view value) +{ + u32(static_cast(value.size())); + const auto raw = as_bytes(value); + bytes_.insert(bytes_.end(), raw.begin(), raw.end()); +} + +void PersistWriter::wstring(std::u16string_view value) +{ + for (const char16_t unit : value) { + bytes_.push_back(static_cast(unit & 0xFFU)); + bytes_.push_back(static_cast(unit >> 8)); + } + bytes_.push_back(std::byte{0}); + bytes_.push_back(std::byte{0}); +} + +void PersistWriter::vector3(const std::array& value) +{ + for (const float v : value) { + f32(v); + } +} + +void PersistWriter::section(std::string_view name, std::span body) +{ + cstring(name); + u32(static_cast(body.size())); + bytes_raw(body); +} + +void PersistWriter::bytes_raw(std::span raw) +{ + bytes_.insert(bytes_.end(), raw.begin(), raw.end()); +} + +} // namespace fable::save diff --git a/rebuild/modern/save/src/Profile.cpp b/rebuild/modern/save/src/Profile.cpp new file mode 100644 index 0000000000..4993cbe08f --- /dev/null +++ b/rebuild/modern/save/src/Profile.cpp @@ -0,0 +1,116 @@ +#include + +#include + +#include +#include +#include + +namespace fable::save { +namespace { + +constexpr std::array magic{ + std::byte{'F'}, std::byte{'a'}, std::byte{'b'}, std::byte{'l'}, std::byte{'e'}, std::byte{'P'}, + std::byte{'r'}, std::byte{'o'}, std::byte{0}, std::byte{0}, std::byte{0}, std::byte{0}, +}; +constexpr std::size_t text_start = 0x10; + +std::uint32_t read_u32(std::span bytes, std::size_t offset) noexcept +{ + return static_cast(bytes[offset]) | static_cast(bytes[offset + 1]) << 8 + | static_cast(bytes[offset + 2]) << 16 | static_cast(bytes[offset + 3]) << 24; +} + +void append_u32(std::vector& out, std::uint32_t value) +{ + for (int i = 0; i < 4; ++i) { + out.push_back(static_cast((value >> (8 * i)) & 0xFFU)); + } +} + +} // namespace + +std::expected Profile::decode(std::span file) +{ + if (file.size() < text_start + 4) { + return std::unexpected(ProfileError::truncated); + } + if (!std::equal(magic.begin(), magic.end(), file.begin())) { + return std::unexpected(ProfileError::bad_magic); + } + const std::size_t total = read_u32(file, 0x0C); + if (total != file.size() - 4 || total < text_start) { + return std::unexpected(ProfileError::data_length_out_of_range); + } + if (read_u32(file, total) != ccrc(file.first(total))) { + return std::unexpected(ProfileError::bad_signature); + } + + const auto text = file.subspan(text_start, total - text_start); + const std::string_view all(reinterpret_cast(text.data()), text.size()); + Profile out; + for (std::size_t at = 0; at < all.size();) { + const auto end = all.find("\r\n", at); + if (end == std::string_view::npos) { + return std::unexpected(ProfileError::bad_line); + } + const std::string_view line = all.substr(at, end - at); + const auto space = line.find(' '); + if (space == std::string_view::npos || space == 0 || !line.ends_with(';')) { + return std::unexpected(ProfileError::bad_line); + } + out.entries_.push_back(Entry{std::string(line.substr(0, space)), + std::string(line.substr(space + 1, line.size() - space - 2))}); + at = end + 2; + } + return out; +} + +std::vector Profile::encode() const +{ + std::vector out(magic.begin(), magic.end()); + append_u32(out, 0); // total_data_len, patched below + for (const Entry& e : entries_) { + for (const char c : e.name + ' ' + e.value + ";\r\n") { + out.push_back(static_cast(c)); + } + } + const auto total = static_cast(out.size()); + for (int i = 0; i < 4; ++i) { + out[0x0C + static_cast(i)] = static_cast((total >> (8 * i)) & 0xFFU); + } + append_u32(out, ccrc(out)); + return out; +} + +std::optional Profile::value(std::string_view name) const noexcept +{ + const auto it = std::find_if(entries_.begin(), entries_.end(), [&](const Entry& e) { return e.name == name; }); + if (it == entries_.end()) { + return std::nullopt; + } + return std::string_view(it->value); +} + +bool Profile::set_value(std::string_view name, std::string value) +{ + const auto it = std::find_if(entries_.begin(), entries_.end(), [&](const Entry& e) { return e.name == name; }); + if (it == entries_.end()) { + return false; + } + it->value = std::move(value); + return true; +} + +std::vector Profile::save_game_names() const +{ + std::vector names; + for (std::size_t slot = 1; slot <= save_slot_count; ++slot) { + const auto raw = value("SaveGameNames" + std::to_string(slot)).value_or(""); + const bool quoted = raw.size() >= 2 && raw.front() == '"' && raw.back() == '"'; + names.emplace_back(quoted ? raw.substr(1, raw.size() - 2) : raw); + } + return names; +} + +} // namespace fable::save diff --git a/rebuild/modern/save/src/Regions.cpp b/rebuild/modern/save/src/Regions.cpp new file mode 100644 index 0000000000..4679f0e723 --- /dev/null +++ b/rebuild/modern/save/src/Regions.cpp @@ -0,0 +1,67 @@ +#include + +namespace fable::save { + +std::expected RegionsSection::decode(std::span section) +{ + PersistReader in(section); + if (const auto tag = in.tag(""); !tag) { + return std::unexpected(tag.error()); + } + const auto table_size = in.u32(); + if (!table_size) { + return std::unexpected(table_size.error()); + } + // A world always has its unwritten element 0; each record is at least 4 tags, a NUL and 3 bools. + if (*table_size == 0 || *table_size - 1 > in.remaining() / 20) { + return std::unexpected(PersistError::bad_section); + } + RegionsSection out; + out.regions.reserve(*table_size - 1); + for (std::uint32_t i = 1; i < *table_size; ++i) { + Region region; + if (const auto tag = in.tag(""); !tag) { + return std::unexpected(tag.error()); + } + auto name = in.cstring(); + if (!name) { + return std::unexpected(name.error()); + } + region.name = std::move(*name); + for (bool* flag : {®ion.appear_on_world_map, ®ion.creature_generation_enabled, + ®ion.sound_themes_enabled}) { + if (const auto tag = in.tag(""); !tag) { + return std::unexpected(tag.error()); + } + const auto value = in.boolean(); + if (!value) { + return std::unexpected(value.error()); + } + *flag = *value; + } + out.regions.push_back(std::move(region)); + } + if (in.remaining() != 0) { + return std::unexpected(PersistError::bad_section); + } + return out; +} + +std::vector RegionsSection::encode() const +{ + PersistWriter out; + out.tag(""); + out.u32(static_cast(regions.size() + 1)); + for (const Region& region : regions) { + out.tag(""); + out.cstring(region.name); + for (const bool flag : {region.appear_on_world_map, region.creature_generation_enabled, + region.sound_themes_enabled}) { + out.tag(""); + out.boolean(flag); + } + } + return out.take(); +} + +} // namespace fable::save diff --git a/rebuild/modern/save/src/SaveBody.cpp b/rebuild/modern/save/src/SaveBody.cpp new file mode 100644 index 0000000000..10d152ce0d --- /dev/null +++ b/rebuild/modern/save/src/SaveBody.cpp @@ -0,0 +1,152 @@ +#include + +#include +#include +#include + +namespace fable::save { +namespace { + +constexpr std::size_t max_section_name = 64; + +// A section name is printable ASCII, so a stray length or tag is never mistaken for one. +bool plausible_name(std::span bytes, std::size_t at) +{ + std::size_t i = at; + for (; i < bytes.size() && i - at <= max_section_name; ++i) { + const auto c = std::to_integer(bytes[i]); + if (c == 0) { + return i > at; + } + if (c < 0x20 || c > 0x7E) { + return false; + } + } + return false; +} + +} // namespace + +std::expected SaveBody::decode(std::span body) +{ + PersistReader in(body); + SaveBody out; + + // The clock is the only content that is not a section; its first tag tells them apart. + if (body.size() >= 4 && !plausible_name(body, 0)) { + GameClock clock; + for (auto [name, target] : {std::pair{"Time", &clock.time}, std::pair{"TimeStep", &clock.time_step}}) { + if (auto t = in.tag(name); !t) { + return std::unexpected(t.error()); + } + const auto v = in.f32(); + if (!v) { + return std::unexpected(v.error()); + } + *target = *v; + } + if (auto t = in.tag("DayCount"); !t) { + return std::unexpected(t.error()); + } + const auto days = in.i32(); + if (!days) { + return std::unexpected(days.error()); + } + clock.day_count = *days; + out.clock = clock; + } + + while (in.remaining() != 0) { + if (!plausible_name(body, in.position())) { + return std::unexpected(PersistError::bad_section); + } + auto name = in.cstring(); + if (!name) { + return std::unexpected(name.error()); + } + const auto length = in.i32(); + if (!length) { + return std::unexpected(length.error()); + } + if (*length < 0) { + return std::unexpected(PersistError::bad_section); + } + const auto bytes = in.raw(static_cast(*length)); + if (!bytes) { + return std::unexpected(PersistError::bad_section); + } + out.sections.push_back(Section{std::move(*name), {bytes->begin(), bytes->end()}}); + } + return out; +} + +std::vector SaveBody::encode() const +{ + PersistWriter out; + if (clock) { + out.tag("Time"); + out.f32(clock->time); + out.tag("TimeStep"); + out.f32(clock->time_step); + out.tag("DayCount"); + out.i32(clock->day_count); + } + for (const Section& s : sections) { + out.section(s.name, s.bytes); + } + return out.take(); +} + +const SaveBody::Section* SaveBody::find(std::string_view name) const noexcept +{ + const auto it = std::find_if(sections.begin(), sections.end(), [&](const Section& s) { return s.name == name; }); + return it == sections.end() ? nullptr : &*it; +} + +bool SaveBody::has_retail_shape() const noexcept +{ + constexpr std::array full{"ENTITIES", "PLAYER", "QUESTS", "REGIONS", "FACTIONS"}; + const auto names_are = [&](std::span expected) { + return std::equal(sections.begin(), sections.end(), expected.begin(), expected.end(), + [](const Section& s, std::string_view n) { return s.name == n; }); + }; + return clock ? names_are(full) : names_are(std::span(full).first(1)); +} + +std::expected PlayerSection::decode(std::span section) +{ + PersistReader in(section); + PlayerSection out; + if (auto t = in.tag("PlayerCharacterUID"); !t) { + return std::unexpected(t.error()); + } + const auto uid = in.u64(); + if (!uid) { + return std::unexpected(uid.error()); + } + out.player_character_uid = *uid; + if (auto t = in.tag("PlayerRegionName"); !t) { + return std::unexpected(t.error()); + } + auto region = in.cstring(); + if (!region) { + return std::unexpected(region.error()); + } + out.player_region_name = std::move(*region); + if (in.remaining() != 0) { + return std::unexpected(PersistError::bad_section); + } + return out; +} + +std::vector PlayerSection::encode() const +{ + PersistWriter out; + out.tag("PlayerCharacterUID"); + out.u64(player_character_uid); + out.tag("PlayerRegionName"); + out.cstring(player_region_name); + return out.take(); +} + +} // namespace fable::save diff --git a/rebuild/modern/save/src/SaveContainer.cpp b/rebuild/modern/save/src/SaveContainer.cpp new file mode 100644 index 0000000000..fdae8e039b --- /dev/null +++ b/rebuild/modern/save/src/SaveContainer.cpp @@ -0,0 +1,166 @@ +#include + +#include "Zlib.hpp" + +#include +#include +#include + +namespace fable::save { +namespace { + +constexpr std::array magic{ + std::byte{'F'}, std::byte{'a'}, std::byte{'b'}, std::byte{'l'}, std::byte{'e'}, std::byte{'S'}, + std::byte{'a'}, std::byte{'v'}, std::byte{'e'}, std::byte{'!'}, std::byte{0}, std::byte{0}, +}; + +constexpr std::array make_crc_table() noexcept +{ + std::array table{}; + for (std::uint32_t n = 0; n < table.size(); ++n) { + std::uint32_t c = n; + for (int bit = 0; bit < 8; ++bit) { + c = (c & 1U) != 0 ? (c >> 1) ^ 0xEDB88320U : c >> 1; + } + table[n] = c; + } + return table; +} + +constexpr auto crc_table = make_crc_table(); + +std::uint32_t read_u32(std::span bytes, std::size_t offset) noexcept +{ + return static_cast(bytes[offset]) + | static_cast(bytes[offset + 1]) << 8 + | static_cast(bytes[offset + 2]) << 16 + | static_cast(bytes[offset + 3]) << 24; +} + +void write_u32(std::vector& out, std::size_t offset, std::uint32_t value) noexcept +{ + for (int i = 0; i < 4; ++i) { + out[offset + static_cast(i)] = static_cast((value >> (8 * i)) & 0xFFU); + } +} + +SaveContainerError to_container_error(detail::ZlibError error) noexcept +{ + switch (error) { + case detail::ZlibError::inflate_failed: return SaveContainerError::inflate_failed; + case detail::ZlibError::size_mismatch: return SaveContainerError::inflated_size_mismatch; + case detail::ZlibError::deflate_failed: return SaveContainerError::deflate_failed; + case detail::ZlibError::too_large: return SaveContainerError::too_large; + } + return SaveContainerError::inflate_failed; +} + +std::expected, SaveContainerError> inflate_exact( + std::span compressed, std::uint32_t expected_size) +{ + return detail::inflate_exact(compressed, expected_size).transform_error(to_container_error); +} + +// Container chunks are deflated at level 9 (SaveContainerCodec::encode). +std::expected, SaveContainerError> deflate_retail(std::span data) +{ + return detail::deflate_retail(data, 9).transform_error(to_container_error); +} + +} // namespace + +std::uint32_t ccrc(std::span data, std::uint32_t seed) noexcept +{ + std::uint32_t crc = seed; + for (const std::byte b : data) { + crc = (crc >> 8) ^ crc_table[(crc ^ static_cast(b)) & 0xFFU]; + } + return crc; +} + +std::expected SaveContainerCodec::decode(std::span file) +{ + if (file.size() < header_size) { + return std::unexpected(SaveContainerError::truncated_header); + } + if (!std::equal(magic.begin(), magic.end(), file.begin())) { + return std::unexpected(SaveContainerError::bad_magic); + } + + // VerifySignature order: bounds, CRC, then slab size and padding. + const std::size_t total = read_u32(file, 0x0C); + if (total < header_size || total > file.size() - 4) { + return std::unexpected(SaveContainerError::data_length_out_of_range); + } + if (read_u32(file, total) != ccrc(file.first(total))) { + return std::unexpected(SaveContainerError::bad_signature); + } + if (file.size() != slab_size) { + return std::unexpected(SaveContainerError::bad_slab_size); + } + const auto padding = file.subspan(total + 4); + if (std::any_of(padding.begin(), padding.end(), [](std::byte b) { return b != std::byte{0}; })) { + return std::unexpected(SaveContainerError::nonzero_padding); + } + + const std::uint32_t header_bytes = read_u32(file, 0x10); + const std::uint32_t body_bytes = read_u32(file, 0x14); + const std::size_t header_zlib = read_u32(file, 0x18); + if (header_zlib > total - header_size || total - header_size - header_zlib < 4) { + return std::unexpected(SaveContainerError::chunk_out_of_bounds); + } + const std::size_t body_zlib_at = header_size + header_zlib + 4; + const std::size_t body_zlib = read_u32(file, header_size + header_zlib); + if (body_zlib > total - body_zlib_at) { + return std::unexpected(SaveContainerError::chunk_out_of_bounds); + } + if (body_zlib_at + body_zlib != total) { + return std::unexpected(SaveContainerError::chunk_layout_mismatch); + } + + auto header = inflate_exact(file.subspan(header_size, header_zlib), header_bytes); + if (!header) { + return std::unexpected(header.error()); + } + auto body = inflate_exact(file.subspan(body_zlib_at, body_zlib), body_bytes); + if (!body) { + return std::unexpected(body.error()); + } + return SaveContainer{std::move(*header), std::move(*body)}; +} + +std::expected, SaveContainerError> SaveContainerCodec::encode(const SaveContainer& container) +{ + if (container.header_section.size() > std::numeric_limits::max() + || container.body_section.size() > std::numeric_limits::max()) { + return std::unexpected(SaveContainerError::too_large); + } + auto header = deflate_retail(container.header_section); + if (!header) { + return std::unexpected(header.error()); + } + auto body = deflate_retail(container.body_section); + if (!body) { + return std::unexpected(body.error()); + } + + const std::size_t total = header_size + header->size() + 4 + body->size(); + if (total + 4 > slab_size) { + return std::unexpected(SaveContainerError::too_large); + } + + std::vector file(slab_size, std::byte{0}); + std::copy(magic.begin(), magic.end(), file.begin()); + write_u32(file, 0x0C, static_cast(total)); + write_u32(file, 0x10, static_cast(container.header_section.size())); + write_u32(file, 0x14, static_cast(container.body_section.size())); + write_u32(file, 0x18, static_cast(header->size())); + std::copy(header->begin(), header->end(), file.begin() + header_size); + const std::size_t body_len_at = header_size + header->size(); + write_u32(file, body_len_at, static_cast(body->size())); + std::copy(body->begin(), body->end(), file.begin() + static_cast(body_len_at + 4)); + write_u32(file, total, ccrc(std::span(file).first(total))); + return file; +} + +} // namespace fable::save diff --git a/rebuild/modern/save/src/SaveHeader.cpp b/rebuild/modern/save/src/SaveHeader.cpp new file mode 100644 index 0000000000..157a076ca1 --- /dev/null +++ b/rebuild/modern/save/src/SaveHeader.cpp @@ -0,0 +1,135 @@ +#include + +namespace fable::save { +namespace { + +// Reads `[tag(name)][value]` with the reader's `method` into `target`; returns the first error. +#define FABLE_READ_FIELD(reader, name, method, target) \ + do { \ + if (auto t = (reader).tag(name); !t) { \ + return std::unexpected(t.error()); \ + } \ + auto v = (reader).method(); \ + if (!v) { \ + return std::unexpected(v.error()); \ + } \ + (target) = std::move(*v); \ + } while (false) + +std::expected read_details(PersistReader& in) +{ + SaveHeader::Details d; + FABLE_READ_FIELD(in, "TeleportingEnabled", boolean, d.teleporting_enabled); + FABLE_READ_FIELD(in, "SavingEnabled", boolean, d.saving_enabled); + FABLE_READ_FIELD(in, "ExperienceSpendingEnabled", boolean, d.experience_spending_enabled); + FABLE_READ_FIELD(in, "CreatureGenerationEnabled", boolean, d.creature_generation_enabled); + FABLE_READ_FIELD(in, "CreatureGenerationDisabledGroups", i32, d.creature_generation_disabled_groups); + FABLE_READ_FIELD(in, "HeroSleepingEnabled", boolean, d.hero_sleeping_enabled); + FABLE_READ_FIELD(in, "MapTableShowQuestCardsOnUsed", boolean, d.map_table_show_quest_cards_on_used); + FABLE_READ_FIELD(in, "MiniMapEnabled", boolean, d.mini_map_enabled); + FABLE_READ_FIELD(in, "MiniMapActiveBeforeDisabled", boolean, d.mini_map_active_before_disabled); + FABLE_READ_FIELD(in, "GuildMasterMessagesEnabled", boolean, d.guild_master_messages_enabled); + FABLE_READ_FIELD(in, "SummonerDeathExplosionAffectsHero", boolean, d.summoner_death_explosion_affects_hero); + FABLE_READ_FIELD(in, "MostRecentSaveType", i32, d.most_recent_save_type); + FABLE_READ_FIELD(in, "MostRecentSaveTypeBeforeManualSave", i32, d.most_recent_save_type_before_manual_save); + FABLE_READ_FIELD(in, "MostRecentManualSaveName", wstring, d.most_recent_manual_save_name); + FABLE_READ_FIELD(in, "SaveGameMarkerPos", vector3, d.save_game_marker_pos); + FABLE_READ_FIELD(in, "SaveGameMarkerAngleXY", f32, d.save_game_marker_angle_xy); + FABLE_READ_FIELD(in, "GuildSealRecallPos", vector3, d.guild_seal_recall_pos); + FABLE_READ_FIELD(in, "GuildSealRecallAngleXY", f32, d.guild_seal_recall_angle_xy); + FABLE_READ_FIELD(in, "CurrentRegionName", cstring, d.current_region_name); + FABLE_READ_FIELD(in, "CurrentRegionMinimapGraphicName", cstring, d.current_region_minimap_graphic_name); + FABLE_READ_FIELD(in, "TotalTimePlayed", f32, d.total_time_played); + return d; +} + +} // namespace + +std::expected SaveHeader::decode(std::span section) +{ + PersistReader outer(section); + const auto body = outer.section("HEADER"); + if (!body) { + return std::unexpected(body.error()); + } + if (outer.remaining() != 0) { + return std::unexpected(PersistError::bad_section); + } + + PersistReader in(*body); + SaveHeader header; + FABLE_READ_FIELD(in, "WorldName", cstring, header.world_name); + FABLE_READ_FIELD(in, "WorldFrame", i32, header.world_frame); + if (in.remaining() != 0) { + auto details = read_details(in); + if (!details) { + return std::unexpected(details.error()); + } + header.details = std::move(*details); + } + if (in.remaining() != 0) { + return std::unexpected(PersistError::bad_section); + } + return header; +} + +#undef FABLE_READ_FIELD + +std::vector SaveHeader::encode() const +{ + PersistWriter out; + out.tag("WorldName"); + out.cstring(world_name); + out.tag("WorldFrame"); + out.i32(world_frame); + if (details) { + const Details& d = *details; + out.tag("TeleportingEnabled"); + out.boolean(d.teleporting_enabled); + out.tag("SavingEnabled"); + out.boolean(d.saving_enabled); + out.tag("ExperienceSpendingEnabled"); + out.boolean(d.experience_spending_enabled); + out.tag("CreatureGenerationEnabled"); + out.boolean(d.creature_generation_enabled); + out.tag("CreatureGenerationDisabledGroups"); + out.i32(d.creature_generation_disabled_groups); + out.tag("HeroSleepingEnabled"); + out.boolean(d.hero_sleeping_enabled); + out.tag("MapTableShowQuestCardsOnUsed"); + out.boolean(d.map_table_show_quest_cards_on_used); + out.tag("MiniMapEnabled"); + out.boolean(d.mini_map_enabled); + out.tag("MiniMapActiveBeforeDisabled"); + out.boolean(d.mini_map_active_before_disabled); + out.tag("GuildMasterMessagesEnabled"); + out.boolean(d.guild_master_messages_enabled); + out.tag("SummonerDeathExplosionAffectsHero"); + out.boolean(d.summoner_death_explosion_affects_hero); + out.tag("MostRecentSaveType"); + out.i32(d.most_recent_save_type); + out.tag("MostRecentSaveTypeBeforeManualSave"); + out.i32(d.most_recent_save_type_before_manual_save); + out.tag("MostRecentManualSaveName"); + out.wstring(d.most_recent_manual_save_name); + out.tag("SaveGameMarkerPos"); + out.vector3(d.save_game_marker_pos); + out.tag("SaveGameMarkerAngleXY"); + out.f32(d.save_game_marker_angle_xy); + out.tag("GuildSealRecallPos"); + out.vector3(d.guild_seal_recall_pos); + out.tag("GuildSealRecallAngleXY"); + out.f32(d.guild_seal_recall_angle_xy); + out.tag("CurrentRegionName"); + out.cstring(d.current_region_name); + out.tag("CurrentRegionMinimapGraphicName"); + out.cstring(d.current_region_minimap_graphic_name); + out.tag("TotalTimePlayed"); + out.f32(d.total_time_played); + } + PersistWriter section; + section.section("HEADER", out.bytes()); + return section.take(); +} + +} // namespace fable::save diff --git a/rebuild/modern/save/src/TextSections.cpp b/rebuild/modern/save/src/TextSections.cpp new file mode 100644 index 0000000000..14d95e8589 --- /dev/null +++ b/rebuild/modern/save/src/TextSections.cpp @@ -0,0 +1,229 @@ +#include + +#include "Lzo.hpp" + +#include + +#include +#include +#include + +namespace fable::save { +namespace { + +constexpr std::size_t raw_tail = 3; +constexpr std::uint32_t max_short_clen = 0x7FFF; + +std::span as_bytes(std::string_view text) +{ + return {reinterpret_cast(text.data()), text.size()}; +} + +std::string as_text(std::span bytes) +{ + std::string out(bytes.size(), '\0'); + std::transform(bytes.begin(), bytes.end(), out.begin(), [](std::byte b) { return static_cast(b); }); + return out; +} + +void write_stored(PersistWriter& out, std::string_view text) +{ + out.u32(static_cast(text.size())); + out.bytes_raw(std::array{std::byte{0}, std::byte{0}}); + out.bytes_raw(as_bytes(text)); +} + +} // namespace + +bool lzo_compressor_available() noexcept +{ + return detail::lzo_compressor_available(); +} + +std::expected read_lz_text(PersistReader& in) +{ + const auto size = in.u32(); + if (!size) { + return std::unexpected(size.error()); + } + if (*size == 0) { + return std::string(); + } + const auto short_clen = in.raw(2); + if (!short_clen) { + return std::unexpected(short_clen.error()); + } + const auto clen16 = static_cast(std::to_integer((*short_clen)[0]) + | std::to_integer((*short_clen)[1]) << 8); + std::int64_t clen = clen16; + if (clen16 == -1) { + const auto wide = in.u32(); + if (!wide) { + return std::unexpected(wide.error()); + } + clen = *wide; + } + if (clen <= 0) { + const auto raw = in.raw(*size); + if (!raw) { + return std::unexpected(raw.error()); + } + return as_text(*raw); + } + if (*size <= raw_tail) { + return std::unexpected(PersistError::bad_section); + } + const auto stream = in.raw(static_cast(clen)); + const auto tail = in.raw(raw_tail); + if (!stream || !tail) { + return std::unexpected(PersistError::truncated); + } + auto head = detail::lzo1x_decompress(*stream, *size - raw_tail); + if (!head) { + return std::unexpected(PersistError::bad_section); + } + std::string text = as_text(*head); + text += as_text(*tail); + return text; +} + +void write_lz_text(PersistWriter& out, std::string_view text) +{ + if (text.empty()) { + out.u32(0); + return; + } + if (text.size() <= raw_tail) { + write_stored(out, text); + return; + } + const std::string_view head = text.substr(0, text.size() - raw_tail); + // Upstream lzo1x_999 (optional GPL link) gives retail's exact bytes; otherwise the MIT encoder + // gives a valid LZO1X stream the retail reader accepts. + auto stream = detail::lzo1x_999_compress(as_bytes(head)); + if (!stream) { + stream = codec::lzo1x_compress(as_bytes(head)); + } + if (!stream || stream->size() >= head.size()) { + write_stored(out, text); + return; + } + out.u32(static_cast(text.size())); + if (stream->size() <= max_short_clen) { + const auto clen = static_cast(stream->size()); + out.bytes_raw(std::array{static_cast(clen & 0xFFU), static_cast(clen >> 8)}); + } else { + out.bytes_raw(std::array{std::byte{0xFF}, std::byte{0xFF}}); + out.u32(static_cast(stream->size())); + } + out.bytes_raw(*stream); + out.bytes_raw(as_bytes(text.substr(text.size() - raw_tail))); +} + +std::expected QuestsSection::decode(std::span section) +{ + PersistReader in(section); + auto text = read_lz_text(in); + if (!text) { + return std::unexpected(text.error()); + } + QuestsSection out; + out.text = std::move(*text); + if (const auto tag = in.tag("NumAcceptedBoasts"); !tag) { + return std::unexpected(tag.error()); + } + const auto count = in.u32(); + if (!count) { + return std::unexpected(count.error()); + } + if (*count > in.remaining() / 19) { // 4 tags, a NUL, a u32 and 2 bools per boast + return std::unexpected(PersistError::bad_section); + } + for (std::uint32_t i = 0; i < *count; ++i) { + const std::string index = std::to_string(i); + AcceptedBoast boast; + auto name = in.tag("BoastScriptName" + index).and_then([&] { return in.cstring(); }); + auto id = in.tag("BoastId" + index).and_then([&] { return in.u32(); }); + auto failed = in.tag("BoastFailed" + index).and_then([&] { return in.boolean(); }); + auto completed = in.tag("BoastCompleted" + index).and_then([&] { return in.boolean(); }); + if (!name || !id || !failed || !completed) { + return std::unexpected(!name ? name.error() : !id ? id.error() : !failed ? failed.error() : completed.error()); + } + boast.script_name = std::move(*name); + boast.id = *id; + boast.failed = *failed; + boast.completed = *completed; + out.accepted_boasts.push_back(std::move(boast)); + } + if (in.remaining() != 0) { + return std::unexpected(PersistError::bad_section); + } + return out; +} + +std::vector QuestsSection::encode() const +{ + PersistWriter out; + write_lz_text(out, text); + out.tag("NumAcceptedBoasts"); + out.u32(static_cast(accepted_boasts.size())); + for (std::size_t i = 0; i < accepted_boasts.size(); ++i) { + const std::string index = std::to_string(i); + const AcceptedBoast& boast = accepted_boasts[i]; + out.tag("BoastScriptName" + index); + out.cstring(boast.script_name); + out.tag("BoastId" + index); + out.u32(boast.id); + out.tag("BoastFailed" + index); + out.boolean(boast.failed); + out.tag("BoastCompleted" + index); + out.boolean(boast.completed); + } + return out.take(); +} + +std::expected FactionsSection::decode(std::span section) +{ + PersistReader in(section); + if (const auto tag = in.tag("NumberOfFactions"); !tag) { + return std::unexpected(tag.error()); + } + const auto count = in.u32(); + if (!count) { + return std::unexpected(count.error()); + } + // Each record is at least a tag and a size. + if (*count > in.remaining() / 8) { + return std::unexpected(PersistError::bad_section); + } + FactionsSection out; + out.factions.reserve(*count); + for (std::uint32_t i = 0; i < *count; ++i) { + if (const auto tag = in.tag("Faction" + std::to_string(i)); !tag) { + return std::unexpected(tag.error()); + } + auto text = read_lz_text(in); + if (!text) { + return std::unexpected(text.error()); + } + out.factions.push_back(std::move(*text)); + } + if (in.remaining() != 0) { + return std::unexpected(PersistError::bad_section); + } + return out; +} + +std::vector FactionsSection::encode() const +{ + PersistWriter out; + out.tag("NumberOfFactions"); + out.u32(static_cast(factions.size())); + for (std::size_t i = 0; i < factions.size(); ++i) { + out.tag("Faction" + std::to_string(i)); + write_lz_text(out, factions[i]); + } + return out.take(); +} + +} // namespace fable::save diff --git a/rebuild/modern/save/src/Zlib.cpp b/rebuild/modern/save/src/Zlib.cpp new file mode 100644 index 0000000000..a3d2c32525 --- /dev/null +++ b/rebuild/modern/save/src/Zlib.cpp @@ -0,0 +1,71 @@ +#include "Zlib.hpp" + +#include + +#include +#include + +namespace fable::save::detail { + +std::expected, ZlibError> inflate_exact( + std::span compressed, std::uint32_t expected_size) +{ + if (compressed.size() > std::numeric_limits::max()) { + return std::unexpected(ZlibError::too_large); + } + z_stream stream{}; + if (inflateInit(&stream) != Z_OK) { + return std::unexpected(ZlibError::inflate_failed); + } + struct Cleanup { + z_stream* state; + ~Cleanup() { inflateEnd(state); } + } cleanup{&stream}; + stream.next_in = reinterpret_cast(const_cast(compressed.data())); + stream.avail_in = static_cast(compressed.size()); + std::vector out; + std::array chunk; + int result = Z_OK; + while (result == Z_OK) { + stream.next_out = reinterpret_cast(chunk.data()); + stream.avail_out = static_cast(chunk.size()); + result = inflate(&stream, Z_NO_FLUSH); + const auto produced = chunk.size() - stream.avail_out; + if (produced > expected_size - out.size()) { + return std::unexpected(ZlibError::size_mismatch); + } + out.insert(out.end(), chunk.begin(), chunk.begin() + static_cast(produced)); + } + if (result != Z_STREAM_END || stream.avail_in != 0) { + return std::unexpected(ZlibError::inflate_failed); + } + if (out.size() != expected_size) { + return std::unexpected(ZlibError::size_mismatch); + } + return out; +} + +std::expected, ZlibError> deflate_retail(std::span data, int level) +{ + if (data.size() > std::numeric_limits::max()) { + return std::unexpected(ZlibError::too_large); + } + z_stream stream{}; + if (deflateInit2(&stream, level, Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY) != Z_OK) { + return std::unexpected(ZlibError::deflate_failed); + } + std::vector out(deflateBound(&stream, static_cast(data.size()))); + stream.next_in = reinterpret_cast(const_cast(data.data())); + stream.avail_in = static_cast(data.size()); + stream.next_out = reinterpret_cast(out.data()); + stream.avail_out = static_cast(out.size()); + const int result = deflate(&stream, Z_FINISH); + out.resize(stream.total_out); + deflateEnd(&stream); + if (result != Z_STREAM_END) { + return std::unexpected(ZlibError::deflate_failed); + } + return out; +} + +} // namespace fable::save::detail diff --git a/rebuild/modern/save/src/Zlib.hpp b/rebuild/modern/save/src/Zlib.hpp new file mode 100644 index 0000000000..57caab86b0 --- /dev/null +++ b/rebuild/modern/save/src/Zlib.hpp @@ -0,0 +1,28 @@ +#pragma once + +// Internal: the two zlib operations the save format needs. Retail drives zlib 1.2.1 through +// CCompressorZlib with a 15-bit window and memLevel 8; only the level differs by layer +// (9 for the container chunks, 1 for SAVED_ENTITIES cells). + +#include +#include +#include +#include +#include + +namespace fable::save::detail { + +enum class ZlibError { + inflate_failed, + size_mismatch, // the stream inflates to a different size than its header field says + deflate_failed, + too_large, +}; + +[[nodiscard]] std::expected, ZlibError> inflate_exact( + std::span compressed, std::uint32_t expected_size); + +[[nodiscard]] std::expected, ZlibError> deflate_retail( + std::span data, int level); + +} // namespace fable::save::detail diff --git a/rebuild/modern/save/tests/SaveContainerTests.cpp b/rebuild/modern/save/tests/SaveContainerTests.cpp new file mode 100644 index 0000000000..a76e0d42eb --- /dev/null +++ b/rebuild/modern/save/tests/SaveContainerTests.cpp @@ -0,0 +1,441 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using fable::save::ccrc; +using fable::save::SaveContainer; +using fable::save::SaveContainerCodec; +using fable::save::SaveContainerError; + +int failures = 0; + +void expect(const bool condition, const std::string_view message) +{ + if (!condition) { + std::cerr << "FAIL: " << message << '\n'; + ++failures; + } +} + +void expect_error(const auto& result, const SaveContainerError error, const std::string_view message) +{ + expect(!result && result.error() == error, message); +} + +std::vector bytes(const std::string_view text) +{ + std::vector out; + for (const char c : text) { + out.push_back(static_cast(c)); + } + return out; +} + +SaveContainer sample() +{ + // Shaped like a retail stream: repetitive tagged fields, so deflate has matches to find. + std::vector body; + for (std::uint32_t i = 0; i < 4000; ++i) { + body.push_back(static_cast(i & 0xFFU)); + body.push_back(static_cast((i * 7U) & 0x3FU)); + body.push_back(std::byte{0x2A}); + } + return SaveContainer{bytes("HEADER section"), body}; +} + +std::uint32_t u32_at(std::span file, std::size_t offset) +{ + return static_cast(file[offset]) | static_cast(file[offset + 1]) << 8 + | static_cast(file[offset + 2]) << 16 | static_cast(file[offset + 3]) << 24; +} + +void set_u32(std::vector& file, std::size_t offset, std::uint32_t value) +{ + for (int i = 0; i < 4; ++i) { + file[offset + static_cast(i)] = static_cast((value >> (8 * i)) & 0xffU); + } +} + +void empty_chunks_round_trip() +{ + const SaveContainer empty{{}, {}}; + const auto file = SaveContainerCodec::encode(empty); + expect(file.has_value(), "empty chunks encode"); + if (file) { + const auto decoded = SaveContainerCodec::decode(*file); + expect(decoded && *decoded == empty, "empty zlib chunks round-trip"); + } +} + +void extra_compressed_bytes_are_rejected() +{ + auto file = *SaveContainerCodec::encode(sample()); + const auto header_size = u32_at(file, 0x18); + const auto total = u32_at(file, 0x0c); + const auto insert_at = SaveContainerCodec::header_size + header_size; + file.insert(file.begin() + static_cast(insert_at), std::byte{0x7f}); + file.resize(SaveContainerCodec::slab_size); + set_u32(file, 0x18, header_size + 1); + set_u32(file, 0x0c, total + 1); + set_u32(file, total + 1, ccrc(std::span(file).first(total + 1))); + expect_error(SaveContainerCodec::decode(file), SaveContainerError::inflate_failed, + "a chunk must consume all of its declared compressed bytes"); +} + +void crc_matches_retail_ccrc() +{ + // Seed 0, no inversion: "123456789" is the classic check input; zlib's crc32 gives + // 0xCBF43926 with pre/post inversion, so the raw seed-0 form differs from it. + expect(ccrc(bytes("123456789")) == 0x2DFD2D88U, "seed-0 CRC of the check string"); + expect(ccrc(bytes("")) == 0U, "empty input keeps the seed"); + expect(ccrc(bytes("a"), 0xFFFFFFFFU) != ccrc(bytes("a")), "seed is honoured"); +} + +void encode_writes_retail_layout() +{ + const auto file = SaveContainerCodec::encode(sample()); + expect(file.has_value(), "sample encodes"); + if (!file) { + return; + } + expect(file->size() == SaveContainerCodec::slab_size, "file is the 0x4B000 slab"); + expect(std::string_view(reinterpret_cast(file->data()), 10) == "FableSave!", "magic"); + const std::uint32_t total = u32_at(*file, 0x0C); + expect(u32_at(*file, 0x10) == sample().header_section.size(), "header size field"); + expect(u32_at(*file, 0x14) == sample().body_section.size(), "body size field"); + expect(std::to_integer((*file)[0x1C]) == 0x78 && std::to_integer((*file)[0x1D]) == 0xDA, + "level-9 zlib stream header 78 DA"); + expect(u32_at(*file, total) == ccrc(std::span(*file).first(total)), "trailer CRC"); +} + +void round_trip_is_exact() +{ + const auto file = SaveContainerCodec::encode(sample()); + const auto decoded = file ? SaveContainerCodec::decode(*file) : std::unexpected(SaveContainerError::deflate_failed); + expect(decoded.has_value() && *decoded == sample(), "decode(encode(x)) == x"); + if (decoded) { + const auto again = SaveContainerCodec::encode(*decoded); + expect(again.has_value() && *again == *file, "encode is deterministic"); + } +} + +void retail_gates_are_enforced() +{ + auto file = *SaveContainerCodec::encode(sample()); + const std::uint32_t total = u32_at(file, 0x0C); + + auto bad = file; + bad[3] = std::byte{'X'}; + expect_error(SaveContainerCodec::decode(bad), SaveContainerError::bad_magic, "magic is checked"); + + bad = file; + bad[0x40] ^= std::byte{1}; + expect_error(SaveContainerCodec::decode(bad), SaveContainerError::bad_signature, "CRC covers the data"); + + bad = file; + bad.push_back(std::byte{0}); + expect_error(SaveContainerCodec::decode(bad), SaveContainerError::bad_slab_size, "exact slab size"); + + bad = file; + bad[total + 8] = std::byte{1}; + expect_error(SaveContainerCodec::decode(bad), SaveContainerError::nonzero_padding, "zero padding"); + + bad = file; + bad[0x0C] = std::byte{0xFF}; + bad[0x0D] = std::byte{0xFF}; + bad[0x0E] = std::byte{0xFF}; + expect_error(SaveContainerCodec::decode(bad), SaveContainerError::data_length_out_of_range, + "total_data_len bounds"); + + expect_error(SaveContainerCodec::decode(std::span(file).first(8)), SaveContainerError::truncated_header, + "short input"); +} + +void inflated_sizes_are_checked() +{ + auto file = *SaveContainerCodec::encode(sample()); + const std::uint32_t total = u32_at(file, 0x0C); + // Claim a larger body, then re-sign so only the size check can catch it. + file[0x14] = static_cast(std::to_integer(file[0x14]) + 1); + const std::uint32_t crc = ccrc(std::span(file).first(total)); + for (int i = 0; i < 4; ++i) { + file[total + static_cast(i)] = static_cast((crc >> (8 * i)) & 0xFFU); + } + expect_error(SaveContainerCodec::decode(file), SaveContainerError::inflated_size_mismatch, + "header size fields must match the streams"); + + // A tiny valid stream cannot justify allocating a header's maximum u32 size. + file = *SaveContainerCodec::encode(sample()); + set_u32(file, 0x14, 0xffffffffU); + set_u32(file, total, ccrc(std::span(file).first(total))); + expect_error(SaveContainerCodec::decode(file), SaveContainerError::inflated_size_mismatch, + "oversized output claims fail without upfront allocation"); + + file = *SaveContainerCodec::encode(sample()); + set_u32(file, 0x14, 0); + set_u32(file, total, ccrc(std::span(file).first(total))); + expect_error(SaveContainerCodec::decode(file), SaveContainerError::inflated_size_mismatch, + "nonempty streams cannot overflow a zero-byte output declaration"); +} + +void oversized_saves_are_refused() +{ + // Incompressible data larger than the slab cannot be written as a retail save. + std::vector noise(SaveContainerCodec::slab_size); + std::uint32_t x = 0x12345678U; + for (auto& b : noise) { + x = x * 1664525U + 1013904223U; + b = static_cast(x >> 24); + } + expect_error(SaveContainerCodec::encode(SaveContainer{{}, noise}), SaveContainerError::too_large, + "slab overflow is an error, not a truncated file"); +} + +void header_round_trips() +{ + using fable::save::PersistError; + using fable::save::SaveHeader; + + SaveHeader full; + full.world_name = "FinalAlbion"; + full.world_frame = 138230; + SaveHeader::Details d; + d.saving_enabled = true; + d.mini_map_enabled = true; + d.most_recent_save_type = 1; + d.most_recent_manual_save_name = u"Manual - Save1"; + d.save_game_marker_pos = {3114.79F, 3000.5F, -12.25F}; + d.current_region_name = "TXT_REGION_GREATWOOD_TELEPORT"; + d.current_region_minimap_graphic_name = "MINIMAP_GREATWOOD"; + full.details = d; + + const auto bytes = full.encode(); + expect(std::string_view(reinterpret_cast(bytes.data()), 7) == std::string_view("HEADER\0", 7), + "section name"); + // First field tag right after the 4-byte section length: WorldName = a8de4f22 (SAVE.md §3.4). + expect(u32_at(bytes, 11) == 0xA8DE4F22U, "WorldName tag"); + const auto back = SaveHeader::decode(bytes); + expect(back.has_value() && *back == full, "full header round-trips"); + + SaveHeader minimal{"FinalAlbion", 740, std::nullopt}; + const auto small = SaveHeader::decode(minimal.encode()); + expect(small.has_value() && *small == minimal, "minimal-context header (fields 0-1 only)"); + + auto broken = bytes; + broken[11] ^= std::byte{1}; + const auto bad = SaveHeader::decode(broken); + expect(!bad && bad.error() == PersistError::tag_mismatch, "tags are checked like retail CheckCRC"); +} + +void body_round_trips() +{ + using fable::save::PersistError; + using fable::save::PlayerSection; + using fable::save::SaveBody; + + const PlayerSection player{0xFFFFFF0000003AE3ULL, "Greatwood"}; + const auto player_bytes = player.encode(); + expect(u32_at(player_bytes, 0) == 0x96ADC827U, "PlayerCharacterUID tag (SAVE.md §3.5)"); + const auto player_back = PlayerSection::decode(player_bytes); + expect(player_back.has_value() && *player_back == player, "PLAYER round-trips"); + + SaveBody full; + full.clock = SaveBody::GameClock{0.393F, 0.000666F, 5}; + full.sections = {{"ENTITIES", bytes("entity stream")}, {"PLAYER", player_bytes}, + {"QUESTS", bytes("q")}, {"REGIONS", {}}, {"FACTIONS", bytes("f")}}; + const auto encoded = full.encode(); + expect(u32_at(encoded, 0) == 0xEEE2E867U, "body opens with the Time tag"); + const auto back = SaveBody::decode(encoded); + expect(back.has_value() && *back == full, "full body round-trips"); + expect(back && back->find("PLAYER") != nullptr && back->find("PLAYER")->bytes == player_bytes, "find"); + + SaveBody minimal; + minimal.sections = {{"ENTITIES", bytes("only")}}; + const auto small = SaveBody::decode(minimal.encode()); + expect(small.has_value() && *small == minimal && !small->clock, "minimal body has no clock"); + expect(full.has_retail_shape() && minimal.has_retail_shape(), "both writer shapes are retail"); + SaveBody renamed = full; + renamed.sections[1].name = "LAYER"; // what a one-byte-short ENTITIES length looks like + expect(!renamed.has_retail_shape(), "unexpected section names are flagged"); + + auto truncated = encoded; + truncated.pop_back(); + const auto bad = SaveBody::decode(truncated); + expect(!bad && bad.error() == PersistError::bad_section, "sections must tile the stream"); +} + +void entities_round_trip() +{ + using fable::save::EntitiesSection; + using fable::save::EntityCell; + using fable::save::SavedEntities; + + SavedEntities saved; + saved.slots.emplace_back(std::nullopt); // empty slot: u64 0 + saved.slots.emplace_back(EntityCell{0x27A89, sample().body_section, {}}); + saved.slots.emplace_back(EntityCell{0x1832A3, {}, {}}); // record with no stream + saved.dead_entities.push_back({0x027A89000000045CULL, 0x321ACEE7U}); + + const auto bytes_out = saved.encode(); + expect(bytes_out.has_value(), "SAVED_ENTITIES encodes"); + if (!bytes_out) { + return; + } + expect(u32_at(*bytes_out, 4) == 3, "slot count"); + expect(u32_at(*bytes_out, 12) == 0 && u32_at(*bytes_out, 16) == 0, "empty slot is u64 0"); + const std::uint32_t clen = u32_at(*bytes_out, 36); + expect(u32_at(*bytes_out, 20) == 29 + clen, "record length = 29 + compressed length"); + expect(std::to_integer((*bytes_out)[48]) == 0x78 && std::to_integer((*bytes_out)[49]) == 0x01, + "cells are zlib level 1 (78 01)"); + const auto back = SavedEntities::decode(*bytes_out); + expect(back.has_value() && *back == saved, "SAVED_ENTITIES round-trips"); + + fable::save::SavedNpcNames names; + names.entries.emplace(); + names.entries->push_back({0x3020DF0000002903ULL, "", u"Brom"}); + names.entries->push_back({0x3020DF000000290BULL, "GROUP", u"Theresa"}); + const auto names_bytes = names.encode(); + expect(u32_at(names_bytes, 0) == 2 && u32_at(names_bytes, 12) == 10, + "an empty NPC group is written as the 10-byte UNATTACHED"); + const auto names_back = fable::save::SavedNpcNames::decode(names_bytes); + expect(names_back.has_value() && *names_back == names, "SAVED_NPC_NAMES round-trips"); + expect(fable::save::SavedNpcNames{}.encode().empty(), "a minimal-context NPC name section is empty"); + + EntitiesSection entities{fable::save::EntitiesHead{2, 0xDE}, saved, names}; + const auto section = entities.encode(); + const auto entities_back = section ? EntitiesSection::decode(*section) : std::unexpected(fable::save::PersistError::truncated); + expect(entities_back.has_value() && *entities_back == entities, "ENTITIES round-trips with its head"); +} + +void text_sections_round_trip() +{ + using fable::save::PersistReader; + using fable::save::PersistWriter; + + // A hand-assembled LZO1X stream: initial run "abc" (0x14), an M2 match of 8 at distance 3 + // (0xE8 0x00), end of stream (0x11 0x00 0x00). Retail frames it as size, i16 clen, stream, + // then the last 3 bytes raw. + const auto blob = bytes(std::string_view("\x0E\x00\x00\x00\x09\x00\x14" "abc\xE8\x00\x11\x00\x00" "XYZ", 18)); + PersistReader in(blob); + const auto text = fable::save::read_lz_text(in); + expect(text.has_value() && *text == "abcabcabcabXYZ" && in.remaining() == 0, "LZO1X blob decodes"); + + std::string noise; + std::uint32_t seed = 12345; + for (int i = 0; i < 70000; ++i) { + seed = seed * 1103515245U + 12345U; + noise.push_back(static_cast('a' + (seed >> 16) % 26)); + } + for (const std::string& sample : {std::string(), std::string("ab"), std::string("abc"), + std::string("Name \"FACTION_HERO\";\nNumberOfAllies 0;\nEndFaction\n"), + std::string(5000, 'q'), noise}) { + PersistWriter out; + fable::save::write_lz_text(out, sample); + const auto written = out.take(); + PersistReader back(written); + const auto again = fable::save::read_lz_text(back); + expect(again.has_value() && *again == sample && back.remaining() == 0, "LZ text round-trips"); + } + + fable::save::FactionsSection factions{{"Name \"FACTION_HERO\";\nEndFaction\n", ""}}; + expect(fable::save::field_tag("NumberOfFactions") == 0x4F0E45C3U, "FACTIONS head tag is NumberOfFactions"); + const auto factions_back = fable::save::FactionsSection::decode(factions.encode()); + expect(factions_back.has_value() && *factions_back == factions, "FACTIONS round-trips"); + fable::save::QuestsSection quests{"START_QUEST_CARDS\nEND_QUEST_CARDS\n", + {{"Q_TraderEscort", 3, false, false}, {"Q_TraderEscort", 1, true, false}}}; + const auto quests_bytes = quests.encode(); + const auto quests_back = fable::save::QuestsSection::decode(quests_bytes); + expect(quests_back.has_value() && *quests_back == quests, "QUESTS round-trips with its boasts"); + // Retail Manual-Save1 starts its tail with the NumAcceptedBoasts tag 0xA4D9A09F. + expect(fable::save::field_tag("NumAcceptedBoasts") == 0xA4D9A09FU, "boast tags are seed-0 field tags"); +} + +void regions_round_trip() +{ + fable::save::RegionsSection regions{{{"LookoutPoint", false, true, true}, {"PicnicArea", true, false, true}}}; + const auto out = regions.encode(); + expect(u32_at(out, 0) == 0 && u32_at(out, 4) == 3, "REGIONS: tag(\"\") is 0 and the table size counts element 0"); + expect(out.size() == 8 + 2 * 20 + 12 + 10, "REGIONS: 20 bytes per record beyond the name"); + const auto back = fable::save::RegionsSection::decode(out); + expect(back.has_value() && *back == regions, "REGIONS round-trips"); +} + +void profile_round_trips() +{ + using fable::save::Profile; + using fable::save::ProfileError; + + std::string text = "ProfileName \"Cornelio\";\r\nSubtitlingEnabled TRUE;\r\n"; + for (int slot = 1; slot <= 50; ++slot) { + text += "SaveGameNames" + std::to_string(slot) + (slot == 1 ? " \"Manual - Save1\";\r\n" : " \"\";\r\n"); + } + std::vector file = bytes("FablePro"); + file.resize(16, std::byte{0}); + const auto body = bytes(text); + file.insert(file.end(), body.begin(), body.end()); + const auto total = static_cast(file.size()); + for (int i = 0; i < 4; ++i) { + file[12 + static_cast(i)] = static_cast((total >> (8 * i)) & 0xFFU); + } + const std::uint32_t crc = ccrc(file); + for (int i = 0; i < 4; ++i) { + file.push_back(static_cast((crc >> (8 * i)) & 0xFFU)); + } + + auto profile = Profile::decode(file); + expect(profile.has_value(), "profile decodes"); + if (!profile) { + return; + } + expect(profile->encode() == file, "profile re-encodes byte for byte"); + expect(profile->value("SubtitlingEnabled") == "TRUE", "raw values"); + const auto names = profile->save_game_names(); + expect(names.size() == 50 && names[0] == "Manual - Save1" && names[1].empty(), "save registry"); + + expect(profile->set_value("SaveGameNames2", "\"Manual - Save2\""), "registry edit"); + const auto edited = Profile::decode(profile->encode()); + expect(edited && edited->save_game_names()[1] == "Manual - Save2", "edited profile re-signs and reloads"); + + auto broken = file; + broken[20] ^= std::byte{1}; + const auto bad = Profile::decode(broken); + expect(!bad && bad.error() == ProfileError::bad_signature, "profile trailer is enforced"); +} + +} // namespace + +int main() +{ + empty_chunks_round_trip(); + extra_compressed_bytes_are_rejected(); + profile_round_trips(); + header_round_trips(); + body_round_trips(); + entities_round_trip(); + text_sections_round_trip(); + regions_round_trip(); + crc_matches_retail_ccrc(); + encode_writes_retail_layout(); + round_trip_is_exact(); + retail_gates_are_enforced(); + inflated_sizes_are_checked(); + oversized_saves_are_refused(); + if (failures == 0) { + std::cout << "fable_save: all tests passed\n"; + } + return failures == 0 ? 0 : 1; +} diff --git a/rebuild/modern/save/tools/fable_save_check.cpp b/rebuild/modern/save/tools/fable_save_check.cpp new file mode 100644 index 0000000000..3373b95b43 --- /dev/null +++ b/rebuild/modern/save/tools/fable_save_check.cpp @@ -0,0 +1,220 @@ +// fable_save_check ... +// +// For each `FableSave!` file: decode with the retail gates, re-encode, and report whether the +// rewrite is byte-identical to the original. `FablePro` profiles get the same check +// (`profile:exact`). Directories are walked recursively; files with other magics are skipped. +// Exit status 1 if any file fails to decode or re-encode. +// +// exact decode passed and encode(decode(file)) == file +// sections decode passed, sections round-trip, but the compressed bytes differ (the file was +// written by a different deflate, e.g. zlib-ng in Python tools) +// rejected decode failed (retail would refuse to load it) +// +// The HEADER section (chunk0), the body's section list (chunk1), PLAYER and ENTITIES (down to +// the inflated SAVED_ENTITIES cells, which are re-deflated) are also decoded and re-encoded; a +// save where one of them does not reproduce byte for byte gets a `header:`, `body:`, `player:` +// or `entities:` verdict instead. +// +// REGIONS is decoded and must re-encode exactly. QUESTS and FACTIONS (LZO text blobs) are decoded too. A build with FABLE_SAVE_WITH_LZO must +// re-encode them byte for byte; without it they re-encode with the MIT encoder, and the check +// is that the result decodes back to the same text (`quests:` / `factions:` verdicts). + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using fable::save::SaveContainerCodec; + +// "" when the section is absent or re-encodes byte for byte, else the verdict. +template +std::string exact_section_verdict(const fable::save::SaveBody& body, std::string_view name, std::string_view prefix) +{ + const auto* raw = body.find(name); + if (raw == nullptr) { + return ""; + } + const auto decoded = Section::decode(raw->bytes); + if (!decoded) { + return std::string(prefix) + "decode_error_" + std::to_string(static_cast(decoded.error())); + } + return decoded->encode() == raw->bytes ? "" : std::string(prefix) + "encode_mismatch"; +} + +// "" when the text section is absent or re-encodes as this build should, else the verdict. +template +std::string text_section_verdict(const fable::save::SaveBody& body, std::string_view name, std::string_view prefix) +{ + const auto* raw = body.find(name); + if (raw == nullptr) { + return ""; + } + const auto decoded = Section::decode(raw->bytes); + if (!decoded) { + return std::string(prefix) + "decode_error_" + std::to_string(static_cast(decoded.error())); + } + const auto encoded = decoded->encode(); + if (fable::save::lzo_compressor_available() && encoded == raw->bytes) { + return ""; + } + const auto again = Section::decode(encoded); + if (!again || *again != *decoded) { + return std::string(prefix) + "round_trip_mismatch"; + } + // Same text, different compressed bytes: not written by retail's lzo1x_999 (e.g. the Python + // tools' store-mode streams), like a whole-file `sections` verdict. + return fable::save::lzo_compressor_available() ? "sections" : ""; +} + +using fable::save::SaveContainerError; + +std::string_view error_name(SaveContainerError error) +{ + switch (error) { + case SaveContainerError::bad_magic: return "bad_magic"; + case SaveContainerError::truncated_header: return "truncated_header"; + case SaveContainerError::data_length_out_of_range: return "data_length_out_of_range"; + case SaveContainerError::bad_signature: return "bad_signature"; + case SaveContainerError::bad_slab_size: return "bad_slab_size"; + case SaveContainerError::nonzero_padding: return "nonzero_padding"; + case SaveContainerError::chunk_out_of_bounds: return "chunk_out_of_bounds"; + case SaveContainerError::chunk_layout_mismatch: return "chunk_layout_mismatch"; + case SaveContainerError::inflate_failed: return "inflate_failed"; + case SaveContainerError::inflated_size_mismatch: return "inflated_size_mismatch"; + case SaveContainerError::deflate_failed: return "deflate_failed"; + case SaveContainerError::too_large: return "too_large"; + } + return "unknown"; +} + +std::vector read_file(const std::filesystem::path& path) +{ + std::ifstream in(path, std::ios::binary); + const std::vector raw{std::istreambuf_iterator(in), std::istreambuf_iterator()}; + std::vector out(raw.size()); + for (std::size_t i = 0; i < raw.size(); ++i) { + out[i] = static_cast(raw[i]); + } + return out; +} + +bool has_magic(const std::vector& file, std::string_view magic) +{ + if (file.size() < magic.size()) { + return false; + } + for (std::size_t i = 0; i < magic.size(); ++i) { + if (file[i] != static_cast(magic[i])) { + return false; + } + } + return true; +} + +} // namespace + +int main(int argc, char** argv) +{ + std::vector files; + for (int i = 1; i < argc; ++i) { + const std::filesystem::path arg = argv[i]; + if (std::filesystem::is_directory(arg)) { + for (const auto& entry : std::filesystem::recursive_directory_iterator(arg)) { + if (entry.is_regular_file()) { + files.push_back(entry.path()); + } + } + } else { + files.push_back(arg); + } + } + + std::map totals; + for (const auto& path : files) { + const auto file = read_file(path); + if (has_magic(file, "FablePro")) { + const auto profile = fable::save::Profile::decode(file); + const std::string verdict = !profile ? "profile:rejected_" + std::to_string(static_cast(profile.error())) + : profile->encode() == file ? "profile:exact" + : "profile:encode_mismatch"; + ++totals[verdict]; + if (verdict != "profile:exact") { + std::cout << verdict << '\t' << path.string() << '\n'; + } + continue; + } + if (!has_magic(file, "FableSave!")) { + continue; + } + const auto decoded = SaveContainerCodec::decode(file); + std::string verdict; + if (!decoded) { + verdict = "rejected:" + std::string(error_name(decoded.error())); + } else if (const auto header = fable::save::SaveHeader::decode(decoded->header_section); !header) { + verdict = "header:decode_error_" + std::to_string(static_cast(header.error())); + } else if (header->encode() != decoded->header_section) { + verdict = "header:encode_mismatch"; + } else if (const auto body = fable::save::SaveBody::decode(decoded->body_section); !body) { + verdict = "body:decode_error_" + std::to_string(static_cast(body.error())); + } else if (body->encode() != decoded->body_section) { + verdict = "body:encode_mismatch"; + } else if (!body->has_retail_shape()) { + verdict = "body:unexpected_sections"; + } else if (const auto* player = body->find("PLAYER"); + player != nullptr && !fable::save::PlayerSection::decode(player->bytes)) { + verdict = "player:decode_error"; + } else if (player != nullptr && fable::save::PlayerSection::decode(player->bytes)->encode() != player->bytes) { + verdict = "player:encode_mismatch"; + } else if (const auto entities = fable::save::EntitiesSection::decode(body->find("ENTITIES")->bytes); + !entities) { + verdict = "entities:decode_error_" + std::to_string(static_cast(entities.error())); + } else if (const auto again = entities->encode(); !again || *again != body->find("ENTITIES")->bytes) { + verdict = "entities:encode_mismatch"; + } else if (verdict = exact_section_verdict(*body, "REGIONS", "regions:"); + !verdict.empty()) { + } else if (verdict = text_section_verdict(*body, "QUESTS", "quests:"); + !verdict.empty()) { + } else if (verdict = text_section_verdict(*body, "FACTIONS", "factions:"); + !verdict.empty()) { + } else { + const auto encoded = SaveContainerCodec::encode(*decoded); + if (!encoded) { + verdict = "encode_failed:" + std::string(error_name(encoded.error())); + } else if (*encoded == file) { + verdict = "exact"; + } else { + const auto again = SaveContainerCodec::decode(*encoded); + verdict = again && *again == *decoded ? "sections" : "encode_mismatch"; + } + } + ++totals[verdict]; + if (verdict != "exact") { + std::cout << verdict << '\t' << path.string() << '\n'; + } + } + std::cout << "text blobs: " << (fable::save::lzo_compressor_available() ? "LZO byte-exact" : "MIT LZO1X encoder (valid, not byte-exact)") + << '\n'; + int rejected = 0; + for (const auto& [verdict, count] : totals) { + std::cout << count << '\t' << verdict << '\n'; + if (verdict != "exact" && verdict != "sections" && verdict != "profile:exact") { + rejected += count; + } + } + return rejected == 0 ? 0 : 1; +} diff --git a/rebuild/modern/texture/CMakeLists.txt b/rebuild/modern/texture/CMakeLists.txt new file mode 100644 index 0000000000..c6d40ba3ea --- /dev/null +++ b/rebuild/modern/texture/CMakeLists.txt @@ -0,0 +1,56 @@ +add_library(fable_texture + src/GbankTexture.cpp +) + +target_include_directories(fable_texture + PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/include" +) +target_link_libraries(fable_texture PRIVATE fable_codec) + +target_compile_features(fable_texture PUBLIC cxx_std_23) +set_target_properties(fable_texture PROPERTIES + CXX_EXTENSIONS OFF +) + +if(MSVC) + set(FABLE_TEXTURE_WARNINGS /W4 /permissive-) +else() + set(FABLE_TEXTURE_WARNINGS -Wall -Wextra -Wconversion -Wpedantic) +endif() +target_compile_options(fable_texture PRIVATE ${FABLE_TEXTURE_WARNINGS}) + +add_executable(fable_texture_dump + tools/fable_texture_dump.cpp +) +target_link_libraries(fable_texture_dump PRIVATE fable_texture fable_bank) +target_compile_options(fable_texture_dump PRIVATE ${FABLE_TEXTURE_WARNINGS}) + +# The title viewer presents through the fork's two planned backends: D3D12 (Windows) and Vulkan +# (when the SDK is found). It is Windows-only for now (Win32 window). +if(WIN32 AND CMAKE_SIZEOF_VOID_P EQUAL 8) # a 64-bit tool: the Vulkan SDK here ships 64-bit libraries + find_package(Vulkan QUIET) + add_executable(fable_title_view + tools/fable_title_view.cpp + tools/TitleViewD3D12.cpp + ) + target_link_libraries(fable_title_view PRIVATE fable_texture fable_bank d3d12 dxgi) + if(Vulkan_FOUND) + target_sources(fable_title_view PRIVATE tools/TitleViewVulkan.cpp) + target_link_libraries(fable_title_view PRIVATE Vulkan::Vulkan) + target_compile_definitions(fable_title_view PRIVATE FABLE_TITLE_VIEW_VULKAN=1) + endif() + target_compile_options(fable_title_view PRIVATE ${FABLE_TEXTURE_WARNINGS}) +endif() + +if(BUILD_TESTING) + add_executable(fable_texture_tests + tests/TextureTests.cpp + ) + target_link_libraries(fable_texture_tests PRIVATE fable_texture) + target_compile_options(fable_texture_tests PRIVATE ${FABLE_TEXTURE_WARNINGS}) + + add_test(NAME fable_texture.Texture + COMMAND fable_texture_tests + ) +endif() diff --git a/rebuild/modern/texture/README.md b/rebuild/modern/texture/README.md new file mode 100644 index 0000000000..2d77066316 --- /dev/null +++ b/rebuild/modern/texture/README.md @@ -0,0 +1,44 @@ +# Textures (GBANK entries in `.big` banks) + +Portable C++23 decoder for retail GBANK textures ([TEXTURE.md](../../../docs/formats/TEXTURE.md)): +the 34-byte info descriptor, mip 0 (LZO1X chunks covering all but the last 3 bytes, then those +3 bytes raw; or all raw when `MipSize0` is 0), and linear A8R8G8B8 (stored BGRA), DXT1 and DXT3, +converted to RGBA8 and cropped to the authored frame size. LZO1X comes from `../codec`; banks +come from `../bank`. A self-contained PNG writer (stored deflate blocks) keeps the module free +of zlib. + +## Verified (2026-10-17) + +`fable_texture_dump --check` decodes mip 0 of every GBANK entry: + +- `frontend.big`: **394/394** (125 A8R8G8B8, 116 DXT1, 153 DXT3). +- `textures.big` (retail backup): **6,319/6,324**. The other 5 use pixel formats 0x18 (1) and + 0x23 (4), which TEXTURE.md §3 lists as unconfirmed; they are reported as unsupported, not + guessed. + +`fable_texture_tests` covers the descriptor, a DXT1 block, BGRA order, the mip-0 chunk framing +and the PNG writer. + +## The title logo through the modern tree + +`fable_title_view [--api d3d12|vulkan] [--seconds N] [--capture ]` (Windows): +1. decodes `FRONTEND_TITLE_01_SPRITE` and `FRONTEND_TITLE_02_SPRITE`, the two 256×128 halves + of the logo; +2. composes them over black; +3. presents them in a 64-bit window through **D3D12** or **Vulkan**, the fork's two planned + backends. `--capture` reads the presented swapchain image back through the same API and + writes it as a PNG. + +The D3D12 and Vulkan captures are pixel-identical (2026-10-17, AMD RX 9060 XT). The Vulkan +backend is built when CMake finds the Vulkan SDK. + +This is the modern tree's first frame: "FABLE / The Lost Chapters", whole. + +The viewer only copies the CPU-composed frame into the swapchain image (no shaders, no RHI). It is a check that the fork's own bank, LZO1X +and texture code produce the right pixels on a 64-bit build, not the fork's renderer +(MODERNIZATION step 4: an RHI with D3D12 and Vulkan backends). + +## Not covered + +- Mips 1..n-1 (stored raw after mip 0) and multi-frame sprites (`FrameCount` 4/16). +- Pixel formats 0x18 and 0x23. diff --git a/rebuild/modern/texture/include/fable/texture/GbankTexture.hpp b/rebuild/modern/texture/include/fable/texture/GbankTexture.hpp new file mode 100644 index 0000000000..bc5917952e --- /dev/null +++ b/rebuild/modern/texture/include/fable/texture/GbankTexture.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace fable::texture { + +enum class TextureError { + bad_info, // the TOC info is not the 34-byte GBANK descriptor + unsupported_format, // a pixel format other than A8R8G8B8, DXT1 or DXT3 + bad_payload, // mip 0 does not decode to FrameDataSize bytes +}; + +// The 34-byte GBANK texture descriptor stored as the TOC entry's info (TEXTURE.md §2). +struct TextureInfo { + std::uint16_t alloc_width = 0; // the power-of-two surface + std::uint16_t alloc_height = 0; + std::uint16_t depth = 0; + std::uint16_t frame_width = 0; // the authored image inside the surface + std::uint16_t frame_height = 0; + std::uint16_t frame_count = 0; + std::uint32_t pixel_format = 0; // 0x01 A8R8G8B8, 0x1F DXT1, 0x20 DXT3 + std::uint8_t transparency = 0; + std::uint8_t mip_levels = 0; + std::uint8_t flags = 0; + std::uint32_t frame_data_size = 0; // uncompressed mip-0 bytes + std::uint32_t mip_size0 = 0; // on-disk mip-0 region; 0 = the payload is all raw + std::array format_class{}; + + [[nodiscard]] static std::expected parse(std::span info); +}; + +enum PixelFormat : std::uint32_t { + a8r8g8b8 = 0x01, + dxt1 = 0x1F, + dxt3 = 0x20, +}; + +// Mip 0 of the first frame as the raw surface bytes (alloc_width × alloc_height, the stored +// pixel format): chunks of LZO1X (u16 clen, 0xFFFF then u32; clen 0 = stored) covering all but +// the last 3 bytes, then those 3 bytes raw (TEXTURE.md §4). +[[nodiscard]] std::expected, TextureError> decode_mip0( + const TextureInfo& info, std::span payload); + +// RGBA8, row-major, top row first. +struct Image { + std::uint32_t width = 0; + std::uint32_t height = 0; + std::vector rgba; +}; + +// Converts a mip-0 surface to RGBA8, cropped to the authored frame size. +[[nodiscard]] std::expected to_rgba(const TextureInfo& info, std::span surface); + +// Writes `image` as a PNG (8-bit RGBA, uncompressed deflate blocks, so no zlib dependency). +[[nodiscard]] std::vector encode_png(const Image& image); + +} // namespace fable::texture diff --git a/rebuild/modern/texture/src/GbankTexture.cpp b/rebuild/modern/texture/src/GbankTexture.cpp new file mode 100644 index 0000000000..ae92fcf336 --- /dev/null +++ b/rebuild/modern/texture/src/GbankTexture.cpp @@ -0,0 +1,296 @@ +#include + +#include + +#include +#include +#include + +namespace fable::texture { +namespace { + +constexpr std::size_t info_size = 34; +constexpr std::size_t raw_tail = 3; + +std::uint32_t u16_at(std::span b, std::size_t at) +{ + return std::to_integer(b[at]) | std::to_integer(b[at + 1]) << 8; +} + +std::uint32_t u32_at(std::span b, std::size_t at) +{ + return u16_at(b, at) | u16_at(b, at + 2) << 16; +} + +// RGB565 to 8-bit channels. +std::array rgb565(std::uint32_t c) +{ + const auto r = (c >> 11) & 31U; + const auto g = (c >> 5) & 63U; + const auto b = c & 31U; + return {static_cast((r << 3) | (r >> 2)), static_cast((g << 2) | (g >> 4)), + static_cast((b << 3) | (b >> 2))}; +} + +// One DXT colour block (8 bytes) into 16 RGBA texels; DXT1 uses the 3-colour + transparent mode +// when c0 <= c1, DXT3 always uses 4 colours (its alpha comes from the separate block). +void decode_colour_block(std::span block, bool dxt1, std::array, 16>& out) +{ + const std::uint32_t c0 = u16_at(block, 0); + const std::uint32_t c1 = u16_at(block, 2); + const auto a = rgb565(c0); + const auto b = rgb565(c1); + std::array, 4> palette{}; + palette[0] = {a[0], a[1], a[2], 255}; + palette[1] = {b[0], b[1], b[2], 255}; + for (int i = 0; i < 3; ++i) { + const auto ai = static_cast(a[static_cast(i)]); + const auto bi = static_cast(b[static_cast(i)]); + if (!dxt1 || c0 > c1) { + palette[2][static_cast(i)] = static_cast((2 * ai + bi) / 3); + palette[3][static_cast(i)] = static_cast((ai + 2 * bi) / 3); + } else { + palette[2][static_cast(i)] = static_cast((ai + bi) / 2); + palette[3][static_cast(i)] = 0; + } + } + palette[2][3] = 255; + palette[3][3] = (!dxt1 || c0 > c1) ? 255 : 0; + const std::uint32_t bits = u32_at(block, 4); + for (std::size_t t = 0; t < 16; ++t) { + out[t] = palette[(bits >> (2 * t)) & 3U]; + } +} + +void put_crc_chunk(std::vector& out, std::string_view type, std::span data) +{ + auto be32 = [&](std::uint32_t v) { + for (int s = 24; s >= 0; s -= 8) { + out.push_back(static_cast((v >> s) & 0xFFU)); + } + }; + be32(static_cast(data.size())); + const std::size_t crc_start = out.size(); + for (const char c : type) { + out.push_back(static_cast(c)); + } + out.insert(out.end(), data.begin(), data.end()); + std::uint32_t crc = 0xFFFFFFFFU; + for (std::size_t i = crc_start; i < out.size(); ++i) { + crc ^= std::to_integer(out[i]); + for (int k = 0; k < 8; ++k) { + crc = (crc >> 1) ^ (0xEDB88320U & (0U - (crc & 1U))); + } + } + be32(crc ^ 0xFFFFFFFFU); +} + +} // namespace + +std::expected TextureInfo::parse(std::span info) +{ + if (info.size() != info_size) { + return std::unexpected(TextureError::bad_info); + } + TextureInfo t; + t.alloc_width = static_cast(u16_at(info, 0)); + t.alloc_height = static_cast(u16_at(info, 2)); + t.depth = static_cast(u16_at(info, 4)); + t.frame_width = static_cast(u16_at(info, 6)); + t.frame_height = static_cast(u16_at(info, 8)); + t.frame_count = static_cast(u16_at(info, 10)); + t.pixel_format = u32_at(info, 12); + t.transparency = std::to_integer(info[16]); + t.mip_levels = std::to_integer(info[17]); + t.flags = std::to_integer(info[18]); + t.frame_data_size = u32_at(info, 20); + t.mip_size0 = u32_at(info, 24); + for (std::size_t i = 0; i < t.format_class.size(); ++i) { + t.format_class[i] = std::to_integer(info[28 + i]); + } + if (t.frame_width > t.alloc_width || t.frame_height > t.alloc_height) { + return std::unexpected(TextureError::bad_info); + } + return t; +} + +std::expected, TextureError> decode_mip0(const TextureInfo& info, std::span payload) +{ + const std::size_t raw_size = info.frame_data_size; + if (info.mip_size0 == 0) { // all raw + if (payload.size() < raw_size) { + return std::unexpected(TextureError::bad_payload); + } + return std::vector(payload.begin(), payload.begin() + static_cast(raw_size)); + } + if (raw_size <= raw_tail || info.mip_size0 > payload.size()) { + return std::unexpected(TextureError::bad_payload); + } + const auto region = payload.first(info.mip_size0); + const std::size_t body = raw_size - raw_tail; + std::vector out; + out.reserve(raw_size); + std::size_t pos = 0; + while (out.size() < body) { + if (pos + 2 > region.size()) { + return std::unexpected(TextureError::bad_payload); + } + std::size_t clen = u16_at(region, pos); + pos += 2; + if (clen == 0xFFFF) { + if (pos + 4 > region.size()) { + return std::unexpected(TextureError::bad_payload); + } + clen = u32_at(region, pos); + pos += 4; + } + if (clen == 0) { // stored: the rest of the body raw + const std::size_t n = body - out.size(); + if (pos + n > region.size()) { + return std::unexpected(TextureError::bad_payload); + } + out.insert(out.end(), region.begin() + static_cast(pos), + region.begin() + static_cast(pos + n)); + pos += n; + break; + } + if (pos + clen > region.size()) { + return std::unexpected(TextureError::bad_payload); + } + // Retail writes one chunk for the whole body; a chunk decodes to what is left. + auto chunk = codec::lzo1x_decompress(region.subspan(pos, clen), body - out.size()); + if (!chunk) { + return std::unexpected(TextureError::bad_payload); + } + out.insert(out.end(), chunk->begin(), chunk->end()); + pos += clen; + } + if (pos + raw_tail != region.size()) { + return std::unexpected(TextureError::bad_payload); + } + out.insert(out.end(), region.begin() + static_cast(pos), region.end()); + return out; +} + +std::expected to_rgba(const TextureInfo& info, std::span surface) +{ + const std::size_t aw = info.alloc_width; + const std::size_t ah = info.alloc_height; + if (info.frame_width > aw || info.frame_height > ah + || aw * ah > std::numeric_limits::max() / 4) { + return std::unexpected(TextureError::bad_info); + } + Image img; + img.width = info.frame_width; + img.height = info.frame_height; + auto put = [&](std::size_t x, std::size_t y, const std::array& px) { + if (x < img.width && y < img.height) { + std::copy(px.begin(), px.end(), img.rgba.begin() + static_cast((y * img.width + x) * 4)); + } + }; + switch (info.pixel_format) { + case a8r8g8b8: { + if (surface.size() < aw * ah * 4) { + return std::unexpected(TextureError::bad_payload); + } + img.rgba.assign(static_cast(img.width) * img.height * 4, 0); + for (std::size_t y = 0; y < ah; ++y) { + for (std::size_t x = 0; x < aw; ++x) { + const std::size_t i = (y * aw + x) * 4; // stored B, G, R, A + put(x, y, {std::to_integer(surface[i + 2]), std::to_integer(surface[i + 1]), + std::to_integer(surface[i]), std::to_integer(surface[i + 3])}); + } + } + return img; + } + case dxt1: + case dxt3: { + const bool dxt1_format = info.pixel_format == dxt1; + const std::size_t block_bytes = dxt1_format ? 8 : 16; + const std::size_t bw = std::max(1, (aw + 3) / 4); + const std::size_t bh = std::max(1, (ah + 3) / 4); + if (surface.size() < bw * bh * block_bytes) { + return std::unexpected(TextureError::bad_payload); + } + img.rgba.assign(static_cast(img.width) * img.height * 4, 0); + std::array, 16> texels{}; + for (std::size_t by = 0; by < bh; ++by) { + for (std::size_t bx = 0; bx < bw; ++bx) { + const auto block = surface.subspan((by * bw + bx) * block_bytes, block_bytes); + decode_colour_block(dxt1_format ? block : block.subspan(8), dxt1_format, texels); + if (!dxt1_format) { // DXT3: 16 explicit 4-bit alphas first + for (std::size_t t = 0; t < 16; ++t) { + const auto nibble = (std::to_integer(block[t / 2]) >> (4 * (t % 2))) & 15U; + texels[t][3] = static_cast(nibble * 17); + } + } + for (std::size_t t = 0; t < 16; ++t) { + put(bx * 4 + t % 4, by * 4 + t / 4, texels[t]); + } + } + } + return img; + } + default: + return std::unexpected(TextureError::unsupported_format); + } +} + +std::vector encode_png(const Image& image) +{ + std::vector out; + constexpr std::array signature{0x89, 'P', 'N', 'G', 0x0D, 0x0A, 0x1A, 0x0A}; + for (const unsigned char c : signature) { + out.push_back(static_cast(c)); + } + std::vector ihdr; + auto be32 = [](std::vector& v, std::uint32_t x) { + for (int s = 24; s >= 0; s -= 8) { + v.push_back(static_cast((x >> s) & 0xFFU)); + } + }; + be32(ihdr, image.width); + be32(ihdr, image.height); + constexpr std::array header_tail{8, 6, 0, 0, 0}; // 8-bit, RGBA, deflate, adaptive filter, no interlace + for (const unsigned char c : header_tail) { + ihdr.push_back(static_cast(c)); + } + put_crc_chunk(out, "IHDR", ihdr); + + // Raw scanlines (filter byte 0) wrapped in a zlib stream of stored deflate blocks. + std::vector raw; + const std::size_t stride = static_cast(image.width) * 4; + for (std::size_t y = 0; y < image.height; ++y) { + raw.push_back(std::byte{0}); + for (std::size_t i = 0; i < stride; ++i) { + raw.push_back(static_cast(image.rgba[y * stride + i])); + } + } + std::vector z{std::byte{0x78}, std::byte{0x01}}; + for (std::size_t pos = 0; pos < raw.size() || pos == 0;) { + const std::size_t n = std::min(65535, raw.size() - pos); + const bool last = pos + n == raw.size(); + z.push_back(std::byte{static_cast(last ? 1 : 0)}); + z.push_back(static_cast(n & 0xFF)); + z.push_back(static_cast(n >> 8)); + z.push_back(static_cast(~n & 0xFF)); + z.push_back(static_cast((~n >> 8) & 0xFF)); + z.insert(z.end(), raw.begin() + static_cast(pos), raw.begin() + static_cast(pos + n)); + pos += n; + if (last) { + break; + } + } + std::uint32_t a = 1; + std::uint32_t b = 0; + for (const std::byte v : raw) { + a = (a + std::to_integer(v)) % 65521U; + b = (b + a) % 65521U; + } + be32(z, (b << 16) | a); + put_crc_chunk(out, "IDAT", z); + put_crc_chunk(out, "IEND", {}); + return out; +} + +} // namespace fable::texture diff --git a/rebuild/modern/texture/tests/TextureTests.cpp b/rebuild/modern/texture/tests/TextureTests.cpp new file mode 100644 index 0000000000..75c5f27858 --- /dev/null +++ b/rebuild/modern/texture/tests/TextureTests.cpp @@ -0,0 +1,140 @@ +#include + +#include +#include +#include +#include +#include +#include + +namespace { + +using fable::texture::TextureInfo; + +int failures = 0; + +void expect(const bool condition, const std::string_view message) +{ + if (!condition) { + std::cerr << "FAIL: " << message << '\n'; + ++failures; + } +} + +std::vector info_bytes(std::uint16_t w, std::uint16_t h, std::uint32_t format, std::uint32_t data_size, + std::uint32_t mip0) +{ + std::vector b(34, std::byte{0}); + auto put16 = [&](std::size_t at, std::uint32_t v) { + b[at] = static_cast(v & 0xFF); + b[at + 1] = static_cast((v >> 8) & 0xFF); + }; + auto put32 = [&](std::size_t at, std::uint32_t v) { + put16(at, v & 0xFFFF); + put16(at + 2, v >> 16); + }; + put16(0, w); + put16(2, h); + put16(6, w); + put16(8, h); + put16(10, 1); + put32(12, format); + b[17] = std::byte{1}; + put32(20, data_size); + put32(24, mip0); + return b; +} + +void info_parses() +{ + const auto info = TextureInfo::parse(info_bytes(256, 128, 0x01, 256 * 128 * 4, 0)); + expect(info && info->alloc_width == 256 && info->frame_height == 128 && info->pixel_format == 0x01, "34-byte info parses"); + expect(!TextureInfo::parse(std::vector(33)), "a short info is rejected"); +} + +void dxt1_block_decodes() +{ + // One 4x4 DXT1 block: c0 = pure red (0xF800), c1 = pure blue (0x001F), all texels index 0 + // except texel 15 = index 1. + const auto info = TextureInfo::parse(info_bytes(4, 4, 0x1F, 8, 0)); + std::vector block{std::byte{0x00}, std::byte{0xF8}, std::byte{0x1F}, std::byte{0x00}, + std::byte{0x00}, std::byte{0x00}, std::byte{0x00}, std::byte{0x40}}; + const auto image = fable::texture::to_rgba(*info, block); + expect(image && image->rgba[0] == 255 && image->rgba[1] == 0 && image->rgba[2] == 0 && image->rgba[3] == 255, + "DXT1 texel 0 is red"); + expect(image && image->rgba[15 * 4 + 2] == 255 && image->rgba[15 * 4] == 0, "DXT1 texel 15 is blue"); +} + +void argb_is_bgra_on_disk() +{ + const auto info = TextureInfo::parse(info_bytes(1, 1, 0x01, 4, 0)); + const std::vector px{std::byte{10}, std::byte{20}, std::byte{30}, std::byte{40}}; // B G R A + const auto image = fable::texture::to_rgba(*info, px); + expect(image && image->rgba == std::vector{30, 20, 10, 40}, "A8R8G8B8 is stored as BGRA"); +} + +void partial_dxt_blocks_cover_the_edges() +{ + for (const auto format : {fable::texture::dxt1, fable::texture::dxt3}) { + const bool dxt1 = format == fable::texture::dxt1; + const std::size_t block_bytes = dxt1 ? 8 : 16; + std::vector surface(4 * block_bytes, std::byte{0}); + const std::array colors{0xf800, 0x07e0, 0x001f, 0xffff}; + for (std::size_t i = 0; i < colors.size(); ++i) { + const std::size_t color_at = i * block_bytes + (dxt1 ? 0 : 8); + surface[color_at] = static_cast(colors[i] & 0xff); + surface[color_at + 1] = static_cast(colors[i] >> 8); + if (!dxt1) { + for (std::size_t a = 0; a < 8; ++a) { + surface[i * block_bytes + a] = std::byte{0xff}; + } + } + } + const auto info = TextureInfo::parse(info_bytes(6, 5, format, static_cast(surface.size()), 0)); + const auto image = fable::texture::to_rgba(*info, surface); + expect(image && image->rgba[(0 * 6 + 5) * 4 + 1] == 255, "partial right DXT block is green"); + expect(image && image->rgba[(4 * 6 + 0) * 4 + 2] == 255, "partial bottom DXT block is blue"); + expect(image && image->rgba[(4 * 6 + 5) * 4] == 255 && image->rgba[(4 * 6 + 5) * 4 + 3] == 255, + "partial corner DXT block is opaque white"); + const auto truncated = fable::texture::to_rgba(*info, std::span(surface).first(surface.size() - 1)); + expect(!truncated && truncated.error() == fable::texture::TextureError::bad_payload, + "truncated partial DXT block is rejected"); + } +} + +void mip0_chunks_decode() +{ + // 8 raw bytes: an LZO1X chunk for the first 5 ("abcde": initial run of 5, end of stream), + // then the last 3 bytes raw. + std::vector payload{std::byte{9}, std::byte{0}, std::byte{0x16}, std::byte{'a'}, std::byte{'b'}, + std::byte{'c'}, std::byte{'d'}, std::byte{'e'}, std::byte{0x11}, std::byte{0}, + std::byte{0}, std::byte{'f'}, std::byte{'g'}, std::byte{'h'}}; + const auto info = TextureInfo::parse(info_bytes(1, 2, 0x01, 8, static_cast(payload.size()))); + const auto surface = fable::texture::decode_mip0(*info, payload); + const std::vector want{std::byte{'a'}, std::byte{'b'}, std::byte{'c'}, std::byte{'d'}, + std::byte{'e'}, std::byte{'f'}, std::byte{'g'}, std::byte{'h'}}; + expect(surface && *surface == want, "mip 0 = LZO1X chunk + 3 raw tail bytes"); +} + +void png_has_signature() +{ + fable::texture::Image img{2, 1, {255, 0, 0, 255, 0, 255, 0, 255}}; + const auto png = fable::texture::encode_png(img); + expect(png.size() > 33 && png[1] == std::byte{'P'} && png[12] == std::byte{'I'}, "PNG signature and IHDR"); +} + +} // namespace + +int main() +{ + info_parses(); + dxt1_block_decodes(); + argb_is_bgra_on_disk(); + partial_dxt_blocks_cover_the_edges(); + mip0_chunks_decode(); + png_has_signature(); + if (failures == 0) { + std::cout << "fable_texture: all tests passed\n"; + } + return failures == 0 ? 0 : 1; +} diff --git a/rebuild/modern/texture/tools/TitleView.hpp b/rebuild/modern/texture/tools/TitleView.hpp new file mode 100644 index 0000000000..21d3f1f372 --- /dev/null +++ b/rebuild/modern/texture/tools/TitleView.hpp @@ -0,0 +1,38 @@ +#pragma once + +// Shared by the fable_title_view backends. The frame is composed on the CPU (the title over +// black, window-sized), so each backend only uploads, copies into its swapchain image and +// presents; --capture reads that swapchain image back through the same API. + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include + +#include + +#include +#include +#include + +namespace title_view { + +struct Options { + double seconds = 0; // 0 = until the window closes + std::optional capture; // PNG of the presented swapchain image +}; + +// Runs the window's message loop and presents `frame` every vsync until it closes or +// options.seconds pass. Returns an error description, or "" on success. +std::string run_d3d12(HWND hwnd, const fable::texture::Image& frame, const Options& options); +std::string run_vulkan(HWND hwnd, HINSTANCE instance, const fable::texture::Image& frame, const Options& options); + +// Pumps messages; false once the window has been destroyed. +bool pump_messages(); + +void write_png(const std::filesystem::path& path, const fable::texture::Image& image); + +} // namespace title_view diff --git a/rebuild/modern/texture/tools/TitleViewD3D12.cpp b/rebuild/modern/texture/tools/TitleViewD3D12.cpp new file mode 100644 index 0000000000..5ba9ad86e8 --- /dev/null +++ b/rebuild/modern/texture/tools/TitleViewD3D12.cpp @@ -0,0 +1,201 @@ +// D3D12 backend for fable_title_view: an upload buffer with the frame (rows padded to the +// 256-byte copy pitch), copied into the current back buffer each frame and presented. + +#include "TitleView.hpp" + +#include +#include + +#include +#include +#include +#include + +namespace title_view { +namespace { + +template +struct Com { + T* p = nullptr; + Com() = default; + Com(const Com&) = delete; + Com& operator=(const Com&) = delete; + ~Com() + { + if (p) { + p->Release(); + } + } + T** put() { return &p; } + T* operator->() const { return p; } +}; + +constexpr UINT buffer_count = 2; + +D3D12_RESOURCE_BARRIER transition(ID3D12Resource* r, D3D12_RESOURCE_STATES from, D3D12_RESOURCE_STATES to) +{ + D3D12_RESOURCE_BARRIER b{}; + b.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + b.Transition.pResource = r; + b.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + b.Transition.StateBefore = from; + b.Transition.StateAfter = to; + return b; +} + +ID3D12Resource* make_buffer(ID3D12Device* device, D3D12_HEAP_TYPE heap, UINT64 size, D3D12_RESOURCE_STATES state) +{ + D3D12_HEAP_PROPERTIES hp{}; + hp.Type = heap; + D3D12_RESOURCE_DESC rd{}; + rd.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + rd.Width = size; + rd.Height = 1; + rd.DepthOrArraySize = 1; + rd.MipLevels = 1; + rd.SampleDesc.Count = 1; + rd.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + ID3D12Resource* r = nullptr; + device->CreateCommittedResource(&hp, D3D12_HEAP_FLAG_NONE, &rd, state, nullptr, __uuidof(ID3D12Resource), + reinterpret_cast(&r)); + return r; +} + +} // namespace + +std::string run_d3d12(HWND hwnd, const fable::texture::Image& frame, const Options& options) +{ + Com factory; + if (FAILED(CreateDXGIFactory1(__uuidof(IDXGIFactory4), reinterpret_cast(factory.put())))) { + return "CreateDXGIFactory1 failed"; + } + Com device; + if (FAILED(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), reinterpret_cast(device.put())))) { + return "D3D12CreateDevice failed"; + } + D3D12_COMMAND_QUEUE_DESC qd{}; + qd.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + Com queue; + device->CreateCommandQueue(&qd, __uuidof(ID3D12CommandQueue), reinterpret_cast(queue.put())); + + DXGI_SWAP_CHAIN_DESC1 sd{}; + sd.Width = frame.width; + sd.Height = frame.height; + sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + sd.SampleDesc.Count = 1; + sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + sd.BufferCount = buffer_count; + sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + Com swap1; + if (FAILED(factory->CreateSwapChainForHwnd(queue.p, hwnd, &sd, nullptr, nullptr, swap1.put()))) { + return "CreateSwapChainForHwnd failed"; + } + Com swap; + swap1->QueryInterface(__uuidof(IDXGISwapChain3), reinterpret_cast(swap.put())); + Com backs[buffer_count]; + for (UINT i = 0; i < buffer_count; ++i) { + swap->GetBuffer(i, __uuidof(ID3D12Resource), reinterpret_cast(backs[i].put())); + } + + // Upload and readback buffers share the copy footprint of a back buffer. + D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint{}; + UINT64 total = 0; + const D3D12_RESOURCE_DESC back_desc = backs[0]->GetDesc(); + device->GetCopyableFootprints(&back_desc, 0, 1, 0, &footprint, nullptr, nullptr, &total); + Com upload; + upload.p = make_buffer(device.p, D3D12_HEAP_TYPE_UPLOAD, total, D3D12_RESOURCE_STATE_GENERIC_READ); + Com readback; + readback.p = make_buffer(device.p, D3D12_HEAP_TYPE_READBACK, total, D3D12_RESOURCE_STATE_COPY_DEST); + if (!upload.p || !readback.p) { + return "buffer creation failed"; + } + std::uint8_t* mapped = nullptr; + upload->Map(0, nullptr, reinterpret_cast(&mapped)); + for (UINT y = 0; y < frame.height; ++y) { + std::memcpy(mapped + static_cast(y) * footprint.Footprint.RowPitch, + &frame.rgba[static_cast(y) * frame.width * 4], frame.width * 4); + } + upload->Unmap(0, nullptr); + + Com alloc; + device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(ID3D12CommandAllocator), reinterpret_cast(alloc.put())); + Com list; + device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, alloc.p, nullptr, __uuidof(ID3D12GraphicsCommandList), + reinterpret_cast(list.put())); + list->Close(); + Com fence; + device->CreateFence(0, D3D12_FENCE_FLAG_NONE, __uuidof(ID3D12Fence), reinterpret_cast(fence.put())); + HANDLE event = CreateEventW(nullptr, FALSE, FALSE, nullptr); + UINT64 fence_value = 0; + auto wait_idle = [&] { + queue->Signal(fence.p, ++fence_value); + if (fence->GetCompletedValue() < fence_value) { + fence->SetEventOnCompletion(fence_value, event); + WaitForSingleObject(event, INFINITE); + } + }; + + const auto start = std::chrono::steady_clock::now(); + bool captured = false; + while (pump_messages()) { + const UINT index = swap->GetCurrentBackBufferIndex(); + ID3D12Resource* back = backs[index].p; + alloc->Reset(); + list->Reset(alloc.p, nullptr); + auto to_copy = transition(back, D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_COPY_DEST); + list->ResourceBarrier(1, &to_copy); + D3D12_TEXTURE_COPY_LOCATION dst{}; + dst.pResource = back; + dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + D3D12_TEXTURE_COPY_LOCATION src{}; + src.pResource = upload.p; + src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + src.PlacedFootprint = footprint; + list->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr); + const bool capture_now = options.capture && !captured; + if (capture_now) { // read the composed back buffer before it is presented + auto to_src = transition(back, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COPY_SOURCE); + list->ResourceBarrier(1, &to_src); + D3D12_TEXTURE_COPY_LOCATION rb{}; + rb.pResource = readback.p; + rb.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + rb.PlacedFootprint = footprint; + list->CopyTextureRegion(&rb, 0, 0, 0, &dst, nullptr); + auto to_present = transition(back, D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_PRESENT); + list->ResourceBarrier(1, &to_present); + } else { + auto to_present = transition(back, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PRESENT); + list->ResourceBarrier(1, &to_present); + } + list->Close(); + ID3D12CommandList* lists[] = {list.p}; + queue->ExecuteCommandLists(1, lists); + swap->Present(1, 0); + wait_idle(); + + if (capture_now) { + std::uint8_t* data = nullptr; + readback->Map(0, nullptr, reinterpret_cast(&data)); + fable::texture::Image shot; + shot.width = frame.width; + shot.height = frame.height; + shot.rgba.resize(frame.rgba.size()); + for (UINT y = 0; y < frame.height; ++y) { + std::memcpy(&shot.rgba[static_cast(y) * frame.width * 4], + data + static_cast(y) * footprint.Footprint.RowPitch, frame.width * 4); + } + readback->Unmap(0, nullptr); + write_png(*options.capture, shot); + captured = true; + } + const double elapsed = std::chrono::duration(std::chrono::steady_clock::now() - start).count(); + if (options.seconds > 0 && elapsed > options.seconds) { + DestroyWindow(hwnd); + } + } + wait_idle(); + CloseHandle(event); + return ""; +} + +} // namespace title_view diff --git a/rebuild/modern/texture/tools/TitleViewVulkan.cpp b/rebuild/modern/texture/tools/TitleViewVulkan.cpp new file mode 100644 index 0000000000..9153096058 --- /dev/null +++ b/rebuild/modern/texture/tools/TitleViewVulkan.cpp @@ -0,0 +1,334 @@ +// Vulkan backend for fable_title_view: a host-visible staging buffer with the frame, copied into +// the acquired swapchain image each frame and presented. + +#include "TitleView.hpp" + +#define VK_USE_PLATFORM_WIN32_KHR +#include + +#include +#include +#include +#include +#include +#include + +namespace title_view { +namespace { + +struct Vk { + VkInstance instance = VK_NULL_HANDLE; + VkSurfaceKHR surface = VK_NULL_HANDLE; + VkDevice device = VK_NULL_HANDLE; + VkSwapchainKHR swapchain = VK_NULL_HANDLE; + VkBuffer staging = VK_NULL_HANDLE, readback = VK_NULL_HANDLE; + VkDeviceMemory staging_mem = VK_NULL_HANDLE, readback_mem = VK_NULL_HANDLE; + VkCommandPool pool = VK_NULL_HANDLE; + VkSemaphore acquired = VK_NULL_HANDLE, done = VK_NULL_HANDLE; + VkFence fence = VK_NULL_HANDLE; + + ~Vk() + { + if (device) { + vkDeviceWaitIdle(device); + vkDestroyFence(device, fence, nullptr); + vkDestroySemaphore(device, done, nullptr); + vkDestroySemaphore(device, acquired, nullptr); + vkDestroyCommandPool(device, pool, nullptr); + vkDestroyBuffer(device, readback, nullptr); + vkFreeMemory(device, readback_mem, nullptr); + vkDestroyBuffer(device, staging, nullptr); + vkFreeMemory(device, staging_mem, nullptr); + vkDestroySwapchainKHR(device, swapchain, nullptr); + vkDestroyDevice(device, nullptr); + } + if (instance) { + vkDestroySurfaceKHR(instance, surface, nullptr); + vkDestroyInstance(instance, nullptr); + } + } +}; + +bool make_buffer(VkPhysicalDevice gpu, VkDevice device, VkDeviceSize size, VkBufferUsageFlags usage, VkBuffer& buffer, + VkDeviceMemory& memory) +{ + VkBufferCreateInfo bi{}; + bi.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bi.size = size; + bi.usage = usage; + if (vkCreateBuffer(device, &bi, nullptr, &buffer) != VK_SUCCESS) { + return false; + } + VkMemoryRequirements req{}; + vkGetBufferMemoryRequirements(device, buffer, &req); + VkPhysicalDeviceMemoryProperties props{}; + vkGetPhysicalDeviceMemoryProperties(gpu, &props); + const VkMemoryPropertyFlags want = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + for (uint32_t i = 0; i < props.memoryTypeCount; ++i) { + if ((req.memoryTypeBits & (1U << i)) && (props.memoryTypes[i].propertyFlags & want) == want) { + VkMemoryAllocateInfo ai{}; + ai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + ai.allocationSize = req.size; + ai.memoryTypeIndex = i; + return vkAllocateMemory(device, &ai, nullptr, &memory) == VK_SUCCESS && + vkBindBufferMemory(device, buffer, memory, 0) == VK_SUCCESS; + } + } + return false; +} + +void barrier(VkCommandBuffer cmd, VkImage image, VkImageLayout from, VkImageLayout to, VkAccessFlags src_access, + VkAccessFlags dst_access) +{ + VkImageMemoryBarrier b{}; + b.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + b.oldLayout = from; + b.newLayout = to; + b.srcAccessMask = src_access; + b.dstAccessMask = dst_access; + b.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + b.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + b.image = image; + b.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, + nullptr, 1, &b); +} + +} // namespace + +std::string run_vulkan(HWND hwnd, HINSTANCE instance, const fable::texture::Image& frame, const Options& options) +{ + Vk vk; + VkApplicationInfo app{}; + app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app.pApplicationName = "fable_title_view"; + app.apiVersion = VK_API_VERSION_1_1; + const char* instance_ext[] = {VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME}; + VkInstanceCreateInfo ici{}; + ici.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + ici.pApplicationInfo = &app; + ici.enabledExtensionCount = 2; + ici.ppEnabledExtensionNames = instance_ext; + if (vkCreateInstance(&ici, nullptr, &vk.instance) != VK_SUCCESS) { + return "vkCreateInstance failed"; + } + VkWin32SurfaceCreateInfoKHR sci{}; + sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + sci.hinstance = instance; + sci.hwnd = hwnd; + if (vkCreateWin32SurfaceKHR(vk.instance, &sci, nullptr, &vk.surface) != VK_SUCCESS) { + return "vkCreateWin32SurfaceKHR failed"; + } + + uint32_t gpu_count = 0; + vkEnumeratePhysicalDevices(vk.instance, &gpu_count, nullptr); + std::vector gpus(gpu_count); + vkEnumeratePhysicalDevices(vk.instance, &gpu_count, gpus.data()); + VkPhysicalDevice gpu = VK_NULL_HANDLE; + uint32_t family = 0; + for (VkPhysicalDevice g : gpus) { + uint32_t n = 0; + vkGetPhysicalDeviceQueueFamilyProperties(g, &n, nullptr); + std::vector fams(n); + vkGetPhysicalDeviceQueueFamilyProperties(g, &n, fams.data()); + for (uint32_t f = 0; f < n && gpu == VK_NULL_HANDLE; ++f) { + VkBool32 present = VK_FALSE; + vkGetPhysicalDeviceSurfaceSupportKHR(g, f, vk.surface, &present); + if ((fams[f].queueFlags & VK_QUEUE_GRAPHICS_BIT) && present) { + gpu = g; + family = f; + } + } + if (gpu != VK_NULL_HANDLE) { + break; + } + } + if (gpu == VK_NULL_HANDLE) { + return "no GPU with a graphics queue that can present to the window"; + } + VkPhysicalDeviceProperties gpu_props{}; + vkGetPhysicalDeviceProperties(gpu, &gpu_props); + + const float priority = 1.0f; + VkDeviceQueueCreateInfo qci{}; + qci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + qci.queueFamilyIndex = family; + qci.queueCount = 1; + qci.pQueuePriorities = &priority; + const char* device_ext[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; + VkDeviceCreateInfo dci{}; + dci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + dci.queueCreateInfoCount = 1; + dci.pQueueCreateInfos = &qci; + dci.enabledExtensionCount = 1; + dci.ppEnabledExtensionNames = device_ext; + if (vkCreateDevice(gpu, &dci, nullptr, &vk.device) != VK_SUCCESS) { + return "vkCreateDevice failed"; + } + VkQueue queue = VK_NULL_HANDLE; + vkGetDeviceQueue(vk.device, family, 0, &queue); + + // Swapchain: an 8-bit UNORM format (RGBA or BGRA), images usable as copy targets and sources. + uint32_t nfmt = 0; + vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, vk.surface, &nfmt, nullptr); + std::vector formats(nfmt); + vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, vk.surface, &nfmt, formats.data()); + VkSurfaceFormatKHR fmt{VK_FORMAT_UNDEFINED, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}; + for (const auto& f : formats) { + if (f.format == VK_FORMAT_R8G8B8A8_UNORM || f.format == VK_FORMAT_B8G8R8A8_UNORM) { + fmt = f; + break; + } + } + if (fmt.format == VK_FORMAT_UNDEFINED) { + return "no 8-bit UNORM swapchain format"; + } + const bool bgra = fmt.format == VK_FORMAT_B8G8R8A8_UNORM; + VkSurfaceCapabilitiesKHR caps{}; + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, vk.surface, &caps); + if (!(caps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) { + return "swapchain images cannot be copy targets"; + } + VkSwapchainCreateInfoKHR swci{}; + swci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swci.surface = vk.surface; + swci.minImageCount = std::max(2U, caps.minImageCount); + swci.imageFormat = fmt.format; + swci.imageColorSpace = fmt.colorSpace; + swci.imageExtent = {frame.width, frame.height}; + swci.imageArrayLayers = 1; + swci.imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | + (caps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT); + swci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swci.preTransform = caps.currentTransform; + swci.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swci.presentMode = VK_PRESENT_MODE_FIFO_KHR; // vsync; always available + swci.clipped = VK_TRUE; + if (vkCreateSwapchainKHR(vk.device, &swci, nullptr, &vk.swapchain) != VK_SUCCESS) { + return "vkCreateSwapchainKHR failed"; + } + const bool can_capture = (swci.imageUsage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) != 0; + uint32_t nimg = 0; + vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &nimg, nullptr); + std::vector images(nimg); + vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &nimg, images.data()); + + // Staging data in the swapchain's channel order. + const VkDeviceSize size = frame.rgba.size(); + if (!make_buffer(gpu, vk.device, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, vk.staging, vk.staging_mem) || + !make_buffer(gpu, vk.device, size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, vk.readback, vk.readback_mem)) { + return "buffer creation failed"; + } + void* mapped = nullptr; + vkMapMemory(vk.device, vk.staging_mem, 0, size, 0, &mapped); + auto* dst = static_cast(mapped); + std::memcpy(dst, frame.rgba.data(), frame.rgba.size()); + if (bgra) { + for (std::size_t i = 0; i < frame.rgba.size(); i += 4) { + std::swap(dst[i], dst[i + 2]); + } + } + vkUnmapMemory(vk.device, vk.staging_mem); + + VkCommandPoolCreateInfo pci{}; + + pci.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + pci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + pci.queueFamilyIndex = family; + vkCreateCommandPool(vk.device, &pci, nullptr, &vk.pool); + VkCommandBufferAllocateInfo cai{}; + cai.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cai.commandPool = vk.pool; + cai.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cai.commandBufferCount = 1; + VkCommandBuffer cmd = VK_NULL_HANDLE; + vkAllocateCommandBuffers(vk.device, &cai, &cmd); + VkSemaphoreCreateInfo semi{}; + semi.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + vkCreateSemaphore(vk.device, &semi, nullptr, &vk.acquired); + vkCreateSemaphore(vk.device, &semi, nullptr, &vk.done); + VkFenceCreateInfo fci{}; + fci.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + vkCreateFence(vk.device, &fci, nullptr, &vk.fence); + + VkBufferImageCopy region{}; + region.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; + region.imageExtent = {frame.width, frame.height, 1}; + + const auto start = std::chrono::steady_clock::now(); + bool captured = false; + while (pump_messages()) { + uint32_t index = 0; + if (vkAcquireNextImageKHR(vk.device, vk.swapchain, UINT64_MAX, vk.acquired, VK_NULL_HANDLE, &index) != VK_SUCCESS) { + break; + } + const VkImage image = images[index]; + const bool capture_now = options.capture && !captured && can_capture; + vkResetCommandBuffer(cmd, 0); + VkCommandBufferBeginInfo bi{}; + bi.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + bi.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + vkBeginCommandBuffer(cmd, &bi); + barrier(cmd, image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT); + vkCmdCopyBufferToImage(cmd, vk.staging, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + VkImageLayout layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + if (capture_now) { + barrier(cmd, image, layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_TRANSFER_READ_BIT); + layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + vkCmdCopyImageToBuffer(cmd, image, layout, vk.readback, 1, ®ion); + } + barrier(cmd, image, layout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_TRANSFER_READ_BIT, 0); + vkEndCommandBuffer(cmd); + + const VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; + VkSubmitInfo si{}; + si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + si.waitSemaphoreCount = 1; + si.pWaitSemaphores = &vk.acquired; + si.pWaitDstStageMask = &wait_stage; + si.commandBufferCount = 1; + si.pCommandBuffers = &cmd; + si.signalSemaphoreCount = 1; + si.pSignalSemaphores = &vk.done; + vkQueueSubmit(queue, 1, &si, vk.fence); + VkPresentInfoKHR pi{}; + pi.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + pi.waitSemaphoreCount = 1; + pi.pWaitSemaphores = &vk.done; + pi.swapchainCount = 1; + pi.pSwapchains = &vk.swapchain; + pi.pImageIndices = &index; + vkQueuePresentKHR(queue, &pi); + vkWaitForFences(vk.device, 1, &vk.fence, VK_TRUE, UINT64_MAX); + vkResetFences(vk.device, 1, &vk.fence); + + if (capture_now) { + void* data = nullptr; + vkMapMemory(vk.device, vk.readback_mem, 0, size, 0, &data); + fable::texture::Image shot; + shot.width = frame.width; + shot.height = frame.height; + shot.rgba.assign(static_cast(data), static_cast(data) + size); + vkUnmapMemory(vk.device, vk.readback_mem); + if (bgra) { + for (std::size_t i = 0; i < shot.rgba.size(); i += 4) { + std::swap(shot.rgba[i], shot.rgba[i + 2]); + } + } + write_png(*options.capture, shot); + captured = true; + } + const double elapsed = std::chrono::duration(std::chrono::steady_clock::now() - start).count(); + if (options.seconds > 0 && elapsed > options.seconds) { + DestroyWindow(hwnd); + } + } + if (options.capture && !captured) { + return can_capture ? "window closed before a frame was captured" : "swapchain images cannot be read back"; + } + (void)gpu_props; + return ""; +} + +} // namespace title_view diff --git a/rebuild/modern/texture/tools/fable_texture_dump.cpp b/rebuild/modern/texture/tools/fable_texture_dump.cpp new file mode 100644 index 0000000000..76e4c840ce --- /dev/null +++ b/rebuild/modern/texture/tools/fable_texture_dump.cpp @@ -0,0 +1,107 @@ +// fable_texture_dump [--check] [--out ] [ENTRY_NAME...] +// +// Decodes GBANK texture entries (mip 0 of frame 0) to PNG files named .png. +// --check decodes every texture entry of every GBANK sub-bank and reports the totals by pixel +// format; exit status 1 if any entry fails. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +std::shared_ptr> read_file(const std::filesystem::path& path) +{ + std::ifstream in(path, std::ios::binary); + auto bytes = std::make_shared>(static_cast(std::filesystem::file_size(path))); + in.read(reinterpret_cast(bytes->data()), static_cast(bytes->size())); + return bytes; +} + +} // namespace + +int main(int argc, char** argv) +{ + if (argc < 2) { + std::cerr << "usage: fable_texture_dump [--check] [--out ] [ENTRY_NAME...]\n"; + return 2; + } + bool check = false; + std::filesystem::path out_dir = "."; + std::set wanted; + for (int i = 2; i < argc; ++i) { + const std::string arg = argv[i]; + if (arg == "--check") { + check = true; + } else if (arg == "--out" && i + 1 < argc) { + out_dir = argv[++i]; + } else { + wanted.insert(arg); + } + } + const auto bank = fable::bank::BigBank::decode(read_file(argv[1])); + if (!bank) { + std::cerr << "not a BIGB bank\n"; + return 1; + } + std::map totals; + int failures = 0; + for (const auto& sub : bank->banks) { + if (sub.name.rfind("GBANK", 0) != 0) { + continue; + } + for (const auto& entry : sub.entries) { + const std::string name(entry.name_text()); + if (!check && !wanted.contains(name)) { + continue; + } + const auto info = fable::texture::TextureInfo::parse(entry.info); + std::string verdict; + if (!info) { + verdict = "bad_info"; + } else { + const auto surface = fable::texture::decode_mip0(*info, entry.payload.bytes()); + const auto image = surface ? fable::texture::to_rgba(*info, *surface) + : std::expected( + std::unexpected(surface.error())); + if (!image) { + verdict = image.error() == fable::texture::TextureError::unsupported_format ? "unsupported_format" + : "bad_payload"; + } else { + verdict = "ok"; + if (wanted.contains(name)) { + std::filesystem::create_directories(out_dir); + const auto png = fable::texture::encode_png(*image); + std::ofstream(out_dir / (name + ".png"), std::ios::binary) + .write(reinterpret_cast(png.data()), static_cast(png.size())); + std::cout << "wrote " << (out_dir / (name + ".png")).string() << " (" << image->width << "x" + << image->height << ")\n"; + } + } + } + const auto fmt = info ? info->pixel_format : 0U; + ++totals[verdict + " fmt=" + std::to_string(fmt)]; + if (verdict != "ok" && verdict != "unsupported_format") { + ++failures; + if (failures <= 10) { + std::cout << verdict << '\t' << sub.name << '\t' << name << '\n'; + } + } + } + } + if (check) { + for (const auto& [k, v] : totals) { + std::cout << v << '\t' << k << '\n'; + } + } + return failures == 0 ? 0 : 1; +} diff --git a/rebuild/modern/texture/tools/fable_title_view.cpp b/rebuild/modern/texture/tools/fable_title_view.cpp new file mode 100644 index 0000000000..4769a240d0 --- /dev/null +++ b/rebuild/modern/texture/tools/fable_title_view.cpp @@ -0,0 +1,182 @@ +// fable_title_view [--api d3d12|vulkan] [--seconds N] [--capture ] +// +// The modern tree's first frame: decodes the two halves of the retail title logo +// (FRONTEND_TITLE_01_SPRITE, FRONTEND_TITLE_02_SPRITE) with this tree's own bank, LZO1X and +// texture code, composes them over the frontend's black, and presents them in a 64-bit window +// through D3D12 or Vulkan, the fork's two planned backends (MODERNIZATION step 4). --capture +// reads the presented swapchain image back through the same API into a PNG. +// +// This proves the pixel path end to end on both APIs. It is not the fork's renderer: no RHI, +// no shaders, just a copy into the swapchain image each frame. + +#include "TitleView.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +namespace title_view { + +bool pump_messages() +{ + MSG msg{}; + while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + return false; + } + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + return true; +} + +void write_png(const std::filesystem::path& path, const fable::texture::Image& image) +{ + const auto png = fable::texture::encode_png(image); + std::ofstream(path, std::ios::binary).write(reinterpret_cast(png.data()), static_cast(png.size())); +} + +#if !defined(FABLE_TITLE_VIEW_VULKAN) +std::string run_vulkan(HWND, HINSTANCE, const fable::texture::Image&, const Options&) +{ + return "this build has no Vulkan (the Vulkan SDK was not found at configure time)"; +} +#endif + +} // namespace title_view + +namespace { + +std::shared_ptr> read_file(const std::filesystem::path& path) +{ + std::ifstream in(path, std::ios::binary); + auto bytes = std::make_shared>(static_cast(std::filesystem::file_size(path))); + in.read(reinterpret_cast(bytes->data()), static_cast(bytes->size())); + return bytes; +} + +std::optional load_sprite(const fable::bank::BigBank& bank, const std::string& name) +{ + for (const auto& sub : bank.banks) { + for (const auto& entry : sub.entries) { + if (entry.name_text() != name) { + continue; + } + const auto info = fable::texture::TextureInfo::parse(entry.info); + if (!info) { + return std::nullopt; + } + const auto surface = fable::texture::decode_mip0(*info, entry.payload.bytes()); + if (!surface) { + return std::nullopt; + } + auto image = fable::texture::to_rgba(*info, *surface); + return image ? std::optional(std::move(*image)) : std::nullopt; + } + } + return std::nullopt; +} + +// The window-sized frame: black, with the two halves side by side at (margin, margin), +// alpha-blended over the black like the frontend draws them. +fable::texture::Image compose(const fable::texture::Image& left, const fable::texture::Image& right, std::uint32_t margin) +{ + fable::texture::Image out; + out.width = left.width + right.width + 2 * margin; + out.height = std::max(left.height, right.height) + 2 * margin; + out.rgba.assign(static_cast(out.width) * out.height * 4, 0); + for (std::size_t i = 3; i < out.rgba.size(); i += 4) { + out.rgba[i] = 255; + } + auto blit = [&](const fable::texture::Image& src, std::uint32_t x0) { + for (std::uint32_t y = 0; y < src.height; ++y) { + for (std::uint32_t x = 0; x < src.width; ++x) { + const auto* s = &src.rgba[(static_cast(y) * src.width + x) * 4]; + auto* d = &out.rgba[(static_cast(y + margin) * out.width + x0 + x) * 4]; + for (int c = 0; c < 3; ++c) { + d[c] = static_cast(s[c] * s[3] / 255); + } + } + } + }; + blit(left, margin); + blit(right, margin + left.width); + return out; +} + +LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM w, LPARAM l) +{ + if (msg == WM_DESTROY) { + PostQuitMessage(0); + return 0; + } + return DefWindowProcW(hwnd, msg, w, l); +} + +} // namespace + +int main(int argc, char** argv) +{ + if (argc < 2) { + std::cerr << "usage: fable_title_view [--api d3d12|vulkan] [--seconds N] [--capture ]\n"; + return 2; + } + std::string api = "d3d12"; + title_view::Options options; + for (int i = 2; i + 1 < argc; ++i) { + const std::string arg = argv[i]; + if (arg == "--api") { + api = argv[++i]; + } else if (arg == "--seconds") { + options.seconds = std::stod(argv[++i]); + } else if (arg == "--capture") { + options.capture = argv[++i]; + } + } + if (api != "d3d12" && api != "vulkan") { + std::cerr << "--api must be d3d12 or vulkan\n"; + return 2; + } + + const auto bank = fable::bank::BigBank::decode(read_file(argv[1])); + if (!bank) { + std::cerr << "not a BIGB bank\n"; + return 1; + } + const auto left = load_sprite(*bank, "FRONTEND_TITLE_01_SPRITE"); + const auto right = load_sprite(*bank, "FRONTEND_TITLE_02_SPRITE"); + if (!left || !right) { + std::cerr << "title sprites not found or not decodable\n"; + return 1; + } + const auto frame = compose(*left, *right, 32); + std::cout << "decoded title " << left->width + right->width << "x" << left->height << "; presenting through " << api << '\n'; + + WNDCLASSW wc{}; + wc.lpfnWndProc = wnd_proc; + wc.hInstance = GetModuleHandleW(nullptr); + wc.lpszClassName = L"FableTitleView"; + wc.hCursor = LoadCursorW(nullptr, MAKEINTRESOURCEW(32512)); // IDC_ARROW + RegisterClassW(&wc); + RECT rc{0, 0, static_cast(frame.width), static_cast(frame.height)}; + const DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; // fixed size, like the frame + AdjustWindowRect(&rc, style, FALSE); + const std::wstring title = api == "d3d12" ? L"Fable: The Lost Chapters - modern tree (x64, D3D12)" + : L"Fable: The Lost Chapters - modern tree (x64, Vulkan)"; + HWND hwnd = CreateWindowW(wc.lpszClassName, title.c_str(), style, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, + rc.bottom - rc.top, nullptr, nullptr, wc.hInstance, nullptr); + ShowWindow(hwnd, SW_SHOW); + + const std::string error = api == "d3d12" ? title_view::run_d3d12(hwnd, frame, options) + : title_view::run_vulkan(hwnd, wc.hInstance, frame, options); + if (!error.empty()) { + std::cerr << api << ": " << error << '\n'; + return 1; + } + return 0; +} diff --git a/tools/decomp_pipeline/tests/test_save_text_blob.py b/tools/decomp_pipeline/tests/test_save_text_blob.py new file mode 100644 index 0000000000..5c354a5c26 --- /dev/null +++ b/tools/decomp_pipeline/tests/test_save_text_blob.py @@ -0,0 +1,96 @@ +import struct +import unittest + +from tools.parse_save_entities import build_lz_text_blob, lz_text_blob + + +class SaveTextBlobTests(unittest.TestCase): + def test_empty_text_has_only_a_zero_length_word(self): + self.assertEqual(build_lz_text_blob(b""), struct.pack(" D3DDevice). +STATIC_INSTANCES = {0x13CA618: "CSystemManager"} +# PDB layouts print a base class as `_padding_` ints; these bases are confirmed by size +# (CRenderStateManager is 14908 bytes, exactly the padding run before CRenderManagerCore's own members; +# CRenderManager lists no members of its own before CRenderManagerCore's 15648 bytes). +BASES = {"CRenderManagerCore": "CRenderStateManager", "CRenderManager": "CRenderManagerCore"} + +# Tier B: device members by retail offset, used where PDB typing cannot reach. +# - 0x3CF0 is the only PDB member at that offset in any class (CRenderManagerCore::PD3DDevice). +# - the others apply only inside functions whose name, in Ego_r.pdb, belongs to that class alone. +# CShaderRenderManager::D3DDevice is +0x1E4 in Ego_r.pdb but +0x1D4 in retail: retail shader-manager +# methods (ApplyVertexShader, SetUserVertexShaderConstant, UpdatePixelShader, ...) push [this+0x1D4] +# as the COM `this` of SetVertexShaderConstantF / SetPixelShader / SetStreamSource calls. +UNIQUE_DEVICE_OFFSETS = {0x3CF0} +CLASS_DEVICE_OFFSETS = {"CRenderManagerCore": {0x4, 0x3CF0}, "CRenderStateManager": {0x4}, + "CDisplayManager": {0x58}, "CShaderRenderManager": {0x1D4}} +EGOR_NAMES = ROOT / "ghidra_out" / "egor_pdb_names.tsv" +RTTI_GLOBALS = ROOT / "ghidra_out" / "rtti_live_globals.tsv" + +REGS32 = ("eax", "ebx", "ecx", "edx", "esi", "edi", "ebp") +VOLATILE = ("eax", "ecx", "edx") +ITERATIONS = 10 + + +def d3d9_interfaces() -> dict[str, list[str]]: + text = D3D9_H.read_text(errors="replace") + out = {} + for m in re.finditer(r"DECLARE_INTERFACE_\((IDirect3D\w+),\s*\w+\)", text): + body = text[m.end():text.index("};", m.end())] + out[m.group(1)] = re.findall(r"STDMETHOD(?:_\([^,]+,\s*|\()\s*(\w+)\)", body) + if len(out.get("IDirect3DDevice9", [])) != 119: + sys.exit("d3d9.h parse failed: IDirect3DDevice9 should have 119 methods") + return out + + +class Layouts: + def __init__(self, path: Path): + self.size: dict[str, int] = {} + self.fields: dict[str, list[tuple[int, str]]] = collections.defaultdict(list) + for line in path.read_text(errors="replace").splitlines(): + c = line.split("\t") + if len(c) < 5: + continue + if c[0] == "@STRUCT": + self.size[c[1]] = int(c[2]) + elif c[4] != "_padding_": + self.fields[c[0]].append((int(c[2]), c[3].strip())) + self.size.setdefault(c[0], int(c[1])) + + def member(self, t: str, off: int, depth: int = 0) -> str | None: + """Type of the member of `t` that starts exactly at `off` (through embedded members).""" + if depth > 6: + return None + base = BASES.get(t) + if base and off < self.size.get(base, 0) and not any(o == off for o, _ in self.fields.get(t, ())): + return self.member(base, off, depth + 1) + best = None + for o, ty in self.fields.get(t, ()): + if o == off and not ty.endswith("]"): + return ty + if o <= off and (best is None or o > best[0]): + best = (o, ty) + if best is None: + return None + o, ty = best + arr = re.fullmatch(r"(.+?)\s*\[(\d+)\]", ty) + if arr: + elem = arr.group(1) + esz = 4 if elem.endswith("*") else self.size.get(elem, 0) + if esz and off - o < esz * int(arr.group(2)): + rel = (off - o) % esz + return elem if rel == 0 else self.member(elem, rel, depth + 1) + return None + if ty in self.size and off - o < self.size[ty]: + return self.member(ty, off - o, depth + 1) + return None + + def load(self, t: str, off: int) -> str | None: + """Type of the dword loaded from `[T* + off]`: an embedded struct yields its first field + (e.g. CCountedPointer::Data).""" + ty = self.member(t, off) + for _ in range(4): + if ty is None or ty.endswith("*") or not (ty in self.fields or ty in self.size): + break + ty = self.member(ty, 0) + return ty + + +def functions() -> list[tuple[int, str, int]]: + out = [] + for line in COVERAGE.read_text(errors="replace").splitlines()[1:]: + c = line.split("\t") + if len(c) >= 5 and c[4] != "1" and int(c[3] or 0) > 0: + out.append((int(c[0], 16), c[1], int(c[3]))) + return out + + +def load_image(): + import pefile + pe = pefile.PE(str(EXE), fast_load=True) + return pe.OPTIONAL_HEADER.ImageBase, pe.get_memory_mapped_image() + + +def class_only_names() -> dict[str, str]: + """Bare method name -> device class, for names that no other class in Ego_r.pdb uses.""" + owners = collections.defaultdict(set) + for line in EGOR_NAMES.read_text(errors="replace").splitlines(): + m = re.match(r"[0-9a-fA-F]+ \?(\w+)@(\w+)@@", line) + if m and not m.group(1).startswith("?"): + owners[m.group(1)].add(m.group(2)) + return {n: next(iter(cs)) for n, cs in owners.items() + if len(cs) == 1 and next(iter(cs)) in CLASS_DEVICE_OFFSETS} + + +def rtti_vtable_methods(base: int, img: bytes, lay: "Layouts") -> dict[int, str]: + """Function -> `this` class from the retail file's own MSVC RTTI. Every vtable is preceded by a + complete-object locator (signature 0, offset 0 = primary vtable) naming its class; each slot that + points into .text is a method of that class. A function shared by several classes' vtables (an + inherited, non-overridden method) gets the smallest class with a PDB layout: its offsets are + valid in every derived class.""" + import pefile + pe = pefile.PE(str(EXE), fast_load=True) + sec = {s.Name.rstrip(b"\0").decode(): (base + s.VirtualAddress, base + s.VirtualAddress + s.Misc_VirtualSize) + for s in pe.sections} + text, rdata = sec[".text"], sec[".rdata"] + u32 = lambda a: int.from_bytes(img[a - base:a - base + 4], "little") + owners: dict[int, set[str]] = collections.defaultdict(set) + for a in range(rdata[0], rdata[1] - 8, 4): + col = u32(a) + if not rdata[0] <= col < rdata[1] - 16 or u32(col) != 0 or u32(col + 4) != 0: + continue + td = u32(col + 12) + if not sec[".data"][0] <= td < sec[".data"][1]: + continue + raw = img[td + 8 - base:td + 8 - base + 128].split(b"\0")[0].decode("latin1") + if not raw.startswith((".?AV", ".?AU")) or not raw.endswith("@@") or "@" in raw[4:-2]: + continue + cls = raw[4:-2] + if cls not in lay.size and cls not in lay.fields: + continue + v = a + 4 + while v < rdata[1] and text[0] <= u32(v) < text[1]: + owners[u32(v)].add(cls) + v += 4 + return {f: min(cs, key=lambda c: lay.size.get(c, 1 << 30)) for f, cs in owners.items()} + + +def pointee(ty: str | None) -> str | None: + if ty and ty.endswith("*") and not ty.endswith("**"): + return ty[:-1].strip() + return None + + +class Census: + def __init__(self): + self.ifaces = d3d9_interfaces() + self.lay = Layouts(LAYOUTS) + self.funcs = functions() + self.base, self.img = load_image() + self.md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_32) + self.md.detail = True + self.globals: dict[int, str] = dict(SEED_GLOBALS) + # Live RTTI sweep (tools/modern/rtti_live_globals.py): typed statics and pointer globals. + self.statics: dict[int, str] = dict(STATIC_INSTANCES) + if RTTI_GLOBALS.exists(): + for line in RTTI_GLOBALS.read_text().splitlines()[1:]: + kind, addr, cls = line.split("\t")[:3] + if cls in self.lay.size or cls in self.lay.fields: + (self.statics if kind == "static" else self.globals).setdefault(int(addr, 16), cls) + self.this_type: dict[int, str] = rtti_vtable_methods(self.base, self.img, self.lay) + self.rtti_methods = set(self.this_type) # seeds that caller agreement must not override + self.returns: dict[int, str] = {} + self.owned = class_only_names() + self.tier_b: set[int] = set() # identities of tier-B device loads + + def scan(self): + calls = [] + callee_this = collections.defaultdict(collections.Counter) + new_globals = collections.defaultdict(collections.Counter) + new_returns = {} + lay, ifaces = self.lay, self.ifaces + for fva, fname, size in self.funcs: + code = self.img[fva - self.base: fva - self.base + size] + # reg -> (type, identity); vt: reg -> (type, identity) of the object whose vtable it holds + val: dict[str, tuple[str, int]] = {} + vt: dict[str, tuple[str, int]] = {} + if fva in self.this_type: + val["ecx"] = (self.this_type[fva], fva) + last_push = None + esp4 = None + dev_offs = set(UNIQUE_DEVICE_OFFSETS) | CLASS_DEVICE_OFFSETS.get(self.owned.get(fname, ""), set()) + ins = list(self.md.disasm(code, fva)) + if (len(ins) == 2 and ins[1].mnemonic == "ret" and ins[0].mnemonic == "mov" + and ins[0].operands[1].type == cx.X86_OP_IMM + and ins[0].operands[1].imm in self.statics): + new_returns[fva] = self.statics[ins[0].operands[1].imm] + if (len(ins) == 2 and ins[1].mnemonic == "ret" and ins[0].mnemonic == "mov" + and fva in self.this_type): + o = ins[0].operands + if (o[0].type == cx.X86_OP_REG and ins[0].reg_name(o[0].reg) == "eax" + and o[1].type == cx.X86_OP_MEM and o[1].mem.index == 0 + and ins[0].reg_name(o[1].mem.base) == "ecx"): + ty = pointee(lay.load(self.this_type[fva], o[1].mem.disp)) + if ty: + new_returns[fva] = ty + for insn in ins: + mn, ops = insn.mnemonic, insn.operands + if mn in ("mov", "lea") and len(ops) == 2 and ops[0].type == cx.X86_OP_MEM and mn == "mov": + m = ops[0].mem + if m.base == 0 and m.index == 0 and ops[1].type == cx.X86_OP_REG: + s = insn.reg_name(ops[1].reg) + if s in val and not val[s][0].startswith("@"): + new_globals[m.disp & 0xFFFFFFFF][val[s][0]] += 1 + elif (m.index == 0 and m.disp == 4 and ops[1].type == cx.X86_OP_REG + and insn.reg_name(m.base) == "esp"): + esp4 = val.get(insn.reg_name(ops[1].reg)) # `this` of a COM tail jump + continue + if (mn == "add" and len(ops) == 2 and ops[0].type == cx.X86_OP_REG + and ops[1].type == cx.X86_OP_IMM and insn.reg_name(ops[0].reg) in val): + # pointer to a member: `add edi, 0x3cf0` then `mov eax, [edi]` + r = insn.reg_name(ops[0].reg) + t, ident = val[r] + vt.pop(r, None) + if t in ifaces: + val.pop(r) + else: + base_t, off0 = (t[1:].rsplit("@", 1)[0], int(t.rsplit("@", 1)[1])) if t.startswith("@") else (t, 0) + val[r] = (f"@{base_t}@{off0 + ops[1].imm}", ident) + continue + if mn in ("mov", "lea") and len(ops) == 2 and ops[0].type == cx.X86_OP_REG: + dst = insn.reg_name(ops[0].reg) + if dst not in REGS32: + continue + src = ops[1] + nv = nvt = None + if src.type == cx.X86_OP_REG and mn == "mov": + s = insn.reg_name(src.reg) + nv, nvt = val.get(s), vt.get(s) + elif src.type == cx.X86_OP_IMM and mn == "mov" and src.imm in self.statics: + nv = (self.statics[src.imm], insn.address) + elif src.type == cx.X86_OP_MEM and src.mem.index == 0: + b = insn.reg_name(src.mem.base) if src.mem.base else None + d = src.mem.disp + if mn == "lea": + if b in val and val[b][0] not in ifaces: + ty = val[b][0] if d == 0 else lay.member(val[b][0], d) + if ty in lay.size or ty in lay.fields: + nv = (ty, insn.address) + elif b is None: + g = self.globals.get(d & 0xFFFFFFFF) + if g: + nv = (g, insn.address) + elif b in val: + t = val[b][0] + if d == 0 and t in ifaces: + nvt = val[b] + elif t.startswith("@"): + if mn == "mov": + bt, o0 = t[1:].rsplit("@", 1) + ty = pointee(lay.load(bt, int(o0) + d)) + if ty: + nv = (ty, insn.address) + else: + ty = pointee(lay.load(t, d)) + if ty: + nv = (ty, insn.address) + if nv is None and nvt is None and mn == "mov" and b and d in dev_offs: + nv = ("IDirect3DDevice9", insn.address) + self.tier_b.add(insn.address) + val.pop(dst, None) + vt.pop(dst, None) + if nv: + val[dst] = nv + if nvt: + vt[dst] = nvt + continue + if mn == "push" and len(ops) == 1: + last_push = insn.reg_name(ops[0].reg) if ops[0].type == cx.X86_OP_REG else None + continue + if mn == "call" and len(ops) == 1: + op = ops[0] + if op.type == cx.X86_OP_MEM and op.mem.base and op.mem.index == 0: + vreg = insn.reg_name(op.mem.base) + obj = vt.get(vreg) + if obj and last_push and val.get(last_push) == obj: + calls.append((fva, fname, insn.address, obj[0], op.mem.disp, + "B" if obj[1] in self.tier_b else "A")) + if (op.type == cx.X86_OP_IMM and "ecx" in val and val["ecx"][0] not in ifaces + and not val["ecx"][0].startswith("@")): + callee_this[op.imm][val["ecx"][0]] += 1 + esp4 = None + for r in VOLATILE: + val.pop(r, None) + vt.pop(r, None) + if op.type == cx.X86_OP_IMM and op.imm in self.returns: + val["eax"] = (self.returns[op.imm], insn.address) + last_push = None + continue + if mn == "jmp" and len(ops) == 1 and ops[0].type == cx.X86_OP_MEM and ops[0].mem.base: + # COM tail call: `mov [esp+4], this ; jmp [vtbl+off]` + obj = vt.get(insn.reg_name(ops[0].mem.base)) + if obj and ops[0].mem.index == 0 and esp4 == obj: + calls.append((fva, fname, insn.address, obj[0], ops[0].mem.disp, + "B" if obj[1] in self.tier_b else "A")) + continue + if mn == "ret": + val.clear(); vt.clear() + continue + if ops and ops[0].type == cx.X86_OP_REG and mn not in ("cmp", "test", "push"): + dst = insn.reg_name(ops[0].reg) + if dst in REGS32: + val.pop(dst, None) + vt.pop(dst, None) + self.callee_this = callee_this # kept for diagnostics + # Learn: `this` types agreed by every typed caller; globals written with one type only. + changed = 0 + for callee, cnt in callee_this.items(): + # Unanimous, or dominant (>= 3 typed callers, >= 80%): one caller mistyped by PDB offset + # drift must not hide a method (ClearRenderTarget 0x9BE420: 5 CDisplayManager, 1 CStateInfo). + t, top = cnt.most_common(1)[0] + total = sum(cnt.values()) + if callee not in self.rtti_methods and (len(cnt) == 1 or (total >= 3 and top >= 0.8 * total)): + if self.this_type.get(callee) != t: + self.this_type[callee] = t + changed += 1 + for g, cnt in new_globals.items(): + if len(cnt) == 1 and g not in self.globals: + self.globals[g] = next(iter(cnt)) + changed += 1 + for f, t in new_returns.items(): + if self.returns.get(f) != t: + self.returns[f] = t + changed += 1 + return calls, changed + + def run(self): + for i in range(ITERATIONS): + calls, changed = self.scan() + print(f"pass {i + 1}: {len(calls)} D3D9 calls, {len(self.this_type)} typed methods, " + f"{len(self.globals)} typed globals, {len(self.returns)} getters, {changed} new facts") + if not changed: + break + return calls + + +def write_report(c: Census, calls) -> None: + ifaces = c.ifaces + per = collections.defaultdict(collections.Counter) + per_funcs = collections.defaultdict(lambda: collections.defaultdict(set)) + dev_funcs = collections.Counter() + sites, bad = [], 0 + tiers = collections.Counter() + for fva, fname, cva, iface, off, tier in sorted(calls, key=lambda t: t[2]): + methods = ifaces[iface] + if off % 4 or off // 4 >= len(methods): + bad += 1 + continue + name = methods[off // 4] + per[iface][name] += 1 + tiers[(iface == "IDirect3DDevice9", tier)] += 1 + per_funcs[iface][name].add(fva) + if iface == "IDirect3DDevice9": + dev_funcs[(fva, fname)] += 1 + sites.append({"function": f"{fva:08X}", "name": fname, "call": f"{cva:08X}", + "interface": iface, "method": name, "tier": tier}) + dev = ifaces["IDirect3DDevice9"] + data = { + "source": "tools/modern/d3d9_device_census.py", + "functions_scanned": len(c.funcs), + "call_sites": len(sites), + "out_of_range_slots": bad, + "seed_globals": {f"{k:08X}": v for k, v in SEED_GLOBALS.items()}, + "static_instances": len(c.statics), + "rtti_typed_methods": len(c.rtti_methods), + "typed_globals": len(c.globals), + "typed_methods": len(c.this_type), + "interfaces": {i: {m: {"call_sites": n, "functions": len(per_funcs[i][m])} + for m, n in per[i].most_common()} for i in sorted(per)}, + "sites": sites, + } + OUT_JSON.write_text(json.dumps(data, indent=1) + "\n") + + used = [m for m in dev if per["IDirect3DDevice9"][m]] + nd = sum(per["IDirect3DDevice9"].values()) + L = [ + "# Direct3D 9 call-site census", + "", + "Generated by `tools/modern/d3d9_device_census.py` from the retail `Fable.exe`. Do not edit by", + "hand; every site is in `D3D9_DEVICE_CENSUS.json`. Sizes the RHI for", + "[MODERNIZATION](MODERNIZATION.md) step 4.", + "", + "Method: capstone over every non-thunk function, with registers typed from the PDB layouts.", + "Seeds: the render-manager pointers `[0x1436E18]` / `[0x13CB508]`, the static `CSystemManager`", + "`0x13CA618`, the live RTTI sweep of globals (`ghidra_out/rtti_live_globals.tsv`) and `this` of", + "every method in a retail RTTI vtable. Types spread into callees (unanimous or >= 80% of >= 3 typed", + "callers), globals, getters, embedded structs and member pointers. A site counts when the same", + "load supplies the vtable and the COM `this` (pushed, or stored to `[esp+4]` for a tail jump).", + "Still a lower bound (stack-spilled pointers are not followed), but against the live frontend", + "trace it finds 54 of 55 call sites ([D3D9_LIVE_CENSUS](D3D9_LIVE_CENSUS.md)). Method order is", + "the DirectX SDK `d3d9.h`.", + "", + f"Scanned {len(c.funcs)} functions; typed {len(c.this_type)} methods and {len(c.globals)} globals.", + f"**IDirect3DDevice9: {nd} call sites in {len(dev_funcs)} functions, {len(used)} of 119 methods.**", + f"Tier A (PDB-typed): {tiers[(True, 'A')]} device sites; tier B (retail device offset: the unique", + f"+0x3CF0, or a class-only method name from Ego_r.pdb): {tiers[(True, 'B')]}. Tier B covers", + "`CShaderRenderManager`, whose retail device member is +0x1D4 although Ego_r.pdb says +0x1E4.", + "", + "## IDirect3DDevice9 methods in use", + "", + "| Slot | Method | Call sites | Functions |", + "| ---: | --- | ---: | ---: |", + ] + for m in sorted(used, key=lambda m: (-per["IDirect3DDevice9"][m], dev.index(m))): + L.append(f"| {dev.index(m)} | `{m}` | {per['IDirect3DDevice9'][m]} | " + f"{len(per_funcs['IDirect3DDevice9'][m])} |") + L += ["", "## IDirect3DDevice9 methods not seen", "", + ", ".join(f"`{m}`" for m in dev if not per["IDirect3DDevice9"][m]), "", + "Not seen is not proof of unused: see the lower-bound note above.", "", + "## Other Direct3D 9 interfaces", "", "| Interface | Methods (call sites) |", "| --- | --- |"] + for i in sorted(per): + if i != "IDirect3DDevice9": + L.append(f"| `{i}` | " + ", ".join(f"`{m}` ({n})" for m, n in per[i].most_common()) + " |") + L += ["", "## Functions with the most device calls", "", "| Function | Name | Device calls |", + "| --- | --- | ---: |"] + for (fva, fname), n in dev_funcs.most_common(40): + L.append(f"| `{fva:08X}` | `{fname}` | {n} |") + OUT_MD.write_text("\n".join(L) + "\n", encoding="utf-8") + print(f"device: {nd} sites, {len(used)} methods, {len(dev_funcs)} functions; all D3D9: {len(sites)}") + + +def main() -> None: + c = Census() + calls = c.run() + write_report(c, calls) + + +if __name__ == "__main__": + main() diff --git a/tools/modern/d3d9_live_census.py b/tools/modern/d3d9_live_census.py new file mode 100644 index 0000000000..53a6782c44 --- /dev/null +++ b/tools/modern/d3d9_live_census.py @@ -0,0 +1,172 @@ +"""Live IDirect3DDevice9 census: launch retail Fable.exe under dbgeng (pybag) and record every device +method's callers. Complements the static lower bound in d3d9_device_census.py. + +How: breakpoint on the instruction in CRenderManagerCore::Init that stores the new device +(0xA0A955 `mov [esi+0x3CF0], edi`). There, read the device vtable and set a breakpoint on each of +the 119 methods (names from d3d9.h). Each hit records the return address ([esp]); a method's +breakpoint is removed after BUDGET hits so the game keeps running at speed. Results are written to +--out every few seconds. + +Safety: DEBUG_PROCESS_DETACH_ON_EXIT is set, so killing this tracer leaves the game running. It +does not touch game files. Launch only when the shared install is free. +""" +from __future__ import annotations + +import argparse +import collections +import json +import os +import struct +import sys +import time +from pathlib import Path + +os.environ["WINDBG_DIR"] = r"C:\Windows\System32" +os.environ["_NT_SYMBOL_PATH"] = "" +sys.stdout.reconfigure(line_buffering=True) + +from pybag import UserDbg # noqa: E402 +from pybag.dbgeng import core as DbgEng # noqa: E402 + +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from d3d9_device_census import d3d9_interfaces # noqa: E402 + +INSTALL = Path(r"C:\Programs\Steam\steamapps\common\Fable The Lost Chapters") +DEVICE_STORE = 0xA0A955 # CRenderManagerCore::Init: mov [esi+0x3CF0], edi +DEVICE_STORE_BYTES = bytes.fromhex("89bef03c0000") +IMAGE_FILE_MACHINE_I386 = 0x14C +RENDER_CORE_GLOBAL = 0x13CB508 # CRenderManagerCore* (live-verified 2026-10-16: [+4] == [+0x3CF0] == device) +BUDGET = 3000 + + +def main() -> None: + ap = argparse.ArgumentParser() + ap.add_argument("--seconds", type=int, default=150) + ap.add_argument("--out", required=True) + ap.add_argument("--attach", action="store_true", + help="attach to the running Fable.exe (Steam relaunches a debugger-created one) and " + "read the device from [[0x1436E18]+0x3CF0]; load-time Create* calls are missed") + args = ap.parse_args() + names = d3d9_interfaces()["IDirect3DDevice9"] + + dbg = UserDbg() + if args.attach: + pids = dbg.pids_by_name("Fable.exe") + if len(pids) != 1: + sys.exit(f"expected one Fable.exe, found {pids}") + dbg.attach(pids[0][0]) + else: + os.chdir(INSTALL) + dbg.create(str(INSTALL / "Fable.exe")) + dbg._client._cli.AddProcessOptions(0x00000001) # DEBUG_PROCESS_DETACH_ON_EXIT + dbg._control.SetEffectiveProcessorType(IMAGE_FILE_MACHINE_I386) + try: + trace(dbg, args, names) + finally: + # A crash without detach leaves every game thread suspended (seen 2026-10-16). + try: + dbg.detach() + print("detached; game left running") + except Exception as e: + print("detach failed:", e) + + +def trace(dbg, args, names) -> None: + got = dbg.read(DEVICE_STORE, len(DEVICE_STORE_BYTES)) + if bytes(got) != DEVICE_STORE_BYTES: + sys.exit(f"unexpected bytes at {DEVICE_STORE:#x}: {bytes(got).hex()}") + + state = {"device": None, "vtable": None, "methods": {}, "t_device": None} + hits = collections.Counter() + sites = collections.defaultdict(collections.Counter) + bp_ids = {} + + def u32(addr): + return struct.unpack("= BUDGET: + try: + d.bd(bp_ids[addr]) + except Exception: + pass + return DbgEng.DEBUG_STATUS_GO + + def on_store(bp, d): + regs = d.reg + dev = regs.edi & 0xFFFFFFFF + vt = u32(dev) + state.update(device=dev, vtable=vt, t_device=time.time()) + slots = [u32(vt + 4 * i) for i in range(len(names))] + by_addr = collections.defaultdict(list) + for i, a in enumerate(slots): + by_addr[a].append(names[i]) + state["methods"] = dict(by_addr) + for a in by_addr: + bp_ids[a] = d.bp(a, handler=lambda b, dd, a=a: on_method(b, dd, a)) + print(f"device {dev:#x} vtable {vt:#x}: {len(by_addr)} distinct method bodies hooked") + return DbgEng.DEBUG_STATUS_GO + + if args.attach: + class _R: # minimal stand-in so on_store can read the live device + pass + core = u32(RENDER_CORE_GLOBAL) + if not core or u32(core + 4) != u32(core + 0x3CF0): + sys.exit(f"render core {core:#x} does not hold one device at +4/+0x3CF0") + fake = _R(); fake.edi = u32(core + 0x3CF0) + class _D: + reg = fake + bp = dbg.bp + on_store(None, _D) + else: + dbg.bp(DEVICE_STORE, handler=on_store, oneshot=True) + + def dump(final=False): + data = { + "device": state["device"] and f"{state['device']:08X}", + "vtable": state["vtable"] and f"{state['vtable']:08X}", + "budget": BUDGET, + "final": final, + "methods": {n: {"hits": hits[n], "budget_exhausted": hits[n] >= BUDGET, + "call_sites": {f"{r:08X}": c for r, c in sites[n].most_common()}} + for n in names if hits[n]}, + "shared_bodies": {f"{a:08X}": v for a, v in state["methods"].items() if len(v) > 1}, + } + Path(args.out).write_text(json.dumps(data, indent=1)) + + t0 = time.time() + last = 0.0 + while time.time() - t0 < args.seconds: + try: + dbg.go(timeout=2) # pybag wait() takes SECONDS (it multiplies by 1000) + except Exception as e: # wait timeout or a non-breakpoint event + msg = str(e) + if "not accessible" in msg: + print("target gone:", msg[:120]) + break + if "timeout" not in msg.lower() and "E_PENDING" not in msg: + print("event:", msg[:200]) + try: + dbg.go_nothandled(timeout=2) + except Exception: + pass + if time.time() - last > 5: + last = time.time() + dump() + print(f"t={time.time() - t0:5.0f}s methods={sum(1 for n in names if hits[n])} " + f"hits={sum(hits.values())}") + dump(final=True) + for a, i in bp_ids.items(): + try: + dbg.bc(i) + except Exception: + pass + + +if __name__ == "__main__": + main() diff --git a/tools/modern/d3d9_live_report.py b/tools/modern/d3d9_live_report.py new file mode 100644 index 0000000000..755f89d4cf --- /dev/null +++ b/tools/modern/d3d9_live_report.py @@ -0,0 +1,113 @@ +"""Report a live IDirect3DDevice9 trace (d3d9_live_census.py) against the static census. + +Usage: python tools/modern/d3d9_live_report.py [--label frontend] +Writes docs/modern/D3D9_LIVE_CENSUS.md and copies the trace to docs/modern/D3D9_LIVE_CENSUS_