Drop the names taken from an external offset header (functions and globals), keeping only names the binary itself proves: _WinMain@16 from the CRT entry call, and Class::Method names from the client's own log strings. The six functions Ghidra missed stay in the map under neutral FUN_ names, justified by vtable/pointer references. NAMES.md, README and ROADMAP no longer point at third-party offset lists. Co-Authored-By: Claude Opus 5.5 <[email protected]>
101 lines
4.5 KiB
Markdown
101 lines
4.5 KiB
Markdown
# EverQuest Decomp/Recomp
|
|
|
|
A byte-for-byte matching decompilation of the EverQuest **Titanium** client
|
|
(`eqgame.exe`, linked 2005-10-31 18:36:26 UTC). The goal is C++ source that
|
|
recompiles with the original toolchain to the exact same machine code, which
|
|
can then be ported, fixed and recompiled freely.
|
|
|
|
This repository contains **no original binaries or game assets**, only decompiled source and tools. You need your own copy of the
|
|
client; the tools check it against the expected hash.
|
|
|
|
| | |
|
|
|---|---|
|
|
| Target | `eqgame.exe`, SHA1 `e8fdc68b46df69dde0094d57fd7ba2cb33aef1c5` |
|
|
| Compiler | MSVC 7.1 (Visual Studio .NET 2003), `cl` 13.10.3077, run under Wine |
|
|
| Flags so far | `/O1 /GX /GS` (size-optimised, EH, stack cookies; confirmed by matching code) |
|
|
| Functions | ~15.3k found by Ghidra: ~8.4k game code, ~6.9k CRT/STL/library |
|
|
|
|
## Setup
|
|
|
|
Requirements: Linux (or WSL/macOS) with `wine`, `python3` + PyYAML, `ninja`,
|
|
`i686-w64-mingw32-objdump` (binutils for mingw), and optionally Ghidra.
|
|
|
|
```sh
|
|
tools/setup.sh /path/to/EverQuest/eqgame.exe # links orig/, fetches MSVC 7.1, verifies both
|
|
python3 tools/eq.py build # compile src/ -> build/obj/
|
|
python3 tools/eq.py check # compare against the original
|
|
python3 tools/eq.py progress
|
|
```
|
|
|
|
## Workflow
|
|
|
|
1. Pick a function: `python3 tools/eq.py todo` lists unmatched game functions,
|
|
smallest first. `build/decomp/<addr>.c` has Ghidra's pseudo-C for it
|
|
(`tools/ghidra/ExportDecomp.java`), and `python3 tools/eq.py diff 0x00401050`
|
|
shows the disassembly.
|
|
2. Write C++ under `src/`, with an annotation directly above each definition:
|
|
```cpp
|
|
// FUNCTION: EQGAME 0x00401050
|
|
void CList::Append(CListNode* node) { ... }
|
|
|
|
// GLOBAL: EQGAME 0x0079c100
|
|
CSpellPointManager* g_pSpellPointManager;
|
|
```
|
|
Annotations on declarations (`extern` globals, called functions) tell the checker
|
|
where referenced symbols live. `// LIBRARY: EQGAME 0xADDR mangled` names CRT
|
|
helpers (see `include/crt_symbols.h`). Add `NONMATCHING` after the address for
|
|
code that is functionally right but not yet byte-exact.
|
|
3. `python3 tools/eq.py build && python3 tools/eq.py check`. The tools pair each
|
|
annotation with the compiled (mangled) symbol, so there's no need to edit
|
|
`symbols.txt` by hand.
|
|
4. Iterate with `diff` until `check` says `MATCH`.
|
|
5. `python3 tools/eq.py link` rebuilds `build/eqgame.exe` from the compiled objects
|
|
and checks its SHA1 against the original. It also prints how much of the
|
|
binary now comes from source. See [docs/ROADMAP.md](docs/ROADMAP.md).
|
|
|
|
### Check statuses
|
|
|
|
| Status | Meaning |
|
|
|---|---|
|
|
| `MATCH` | every byte equal, every relocation points at the right symbol |
|
|
| `MATCH?` | bytes equal, but some referenced symbols have no address yet (annotate them, or `check --learn`) |
|
|
| `NONMAT` | marked `NONMATCHING`, still differs |
|
|
| `BADREF` | bytes equal, but a call/global resolves to a different address than the map says |
|
|
| `DIFF` | code differs |
|
|
|
|
Relocated fields (calls, globals, string literals) are masked when comparing
|
|
bytes and then verified separately: against the symbol map for named symbols,
|
|
and by content for string literals and float constants.
|
|
|
|
## Layout
|
|
|
|
```
|
|
config/eqgame/config.yaml target hash, compiler flags
|
|
config/eqgame/symbols.txt ADDR SIZE KIND NAME for every known function/global
|
|
src/ decompiled source (one .cpp per original translation unit, eventually)
|
|
include/ shared headers
|
|
tools/eq.py build / check / diff / progress / map
|
|
tools/binfmt.py PE + COFF readers
|
|
tools/cl.sh MSVC 7.1 under Wine
|
|
tools/ghidra/ headless export used to seed symbols.txt
|
|
orig/ your eqgame.exe (git-ignored)
|
|
```
|
|
|
|
Per-file compiler flags: put `// CFLAGS: /O2` (etc.) near the top of a source file.
|
|
|
|
### Re-seeding the symbol map from Ghidra
|
|
|
|
```sh
|
|
/opt/ghidra/support/analyzeHeadless build/ghidra eqgame -import orig/eqgame.exe \
|
|
-scriptPath tools/ghidra -postScript ExportFunctions.java "$PWD/build/ghidra_functions.txt"
|
|
python3 tools/eq.py import-ghidra build/ghidra_functions.txt # keeps existing names
|
|
```
|
|
|
|
## Notes
|
|
|
|
- The Rich header shows ~640 C++ objects from cl 13.10.3077, plus libraries built
|
|
with VC6 (build 8447/9178), likely Miles Sound System, DirectX helpers, etc.
|
|
Those are classified as `lib` and excluded from progress.
|
|
- Names are placeholders until identified from the binary itself (its own log
|
|
strings and code structure); see `config/eqgame/NAMES.md`.
|