5.6 KiB
5.6 KiB
DBCServer_Release_x64.exe — porting spec (Win32 → Linux)
This was kept from the removed Linux reconstruction src/dbc_server/
(removed 2026-10-15). It records the facts about the original binary that a
re-implementation needs, and the nearest Linux equivalent for each Win32 call.
Sources: evidence.md, docs/DATABASE.md, docs/CONFIG.md. Static analysis
only.
Structure to reproduce
peer ──SERVERS2DBC RMI {rmi_id, …}──▶ stub handler (e.g. 0x140050d20: ++per-RMI counter)
│ push {size, cmd 1001–1317, args}
▼
write queue [idx < 1000] (0x140144730; else "Invalid Queue Index")
│ critical section + semaphore, 1 worker per queue
▼
worker switch (cmd - 1001) (0x1400a8190, jump table 0x1400a9dd4)
▼
CDBLobbyWriteQuery::DBReq* (e.g. 0x1400acbb0) — ADODB.Command "dbo.up_xxx"
▼
reply via DBC2SERVERS proxy (76 RMIs, 0x2af9–0x2b46)
- Data tables:
- The RMI → command → procedure mapping is in
rmi_to_proc.tsv, and the command dispatch incmd_dispatch.tsv. - Procedure parameters (names, SQL types, OUTPUT flags) are in
decomp/schema/<db>/routines.tsv. decomp/scripts/gen_dbc_tables.pyjoins them intoprocs_used.tsv.
- The RMI → command → procedure mapping is in
- Queue array: a 0x1f48-byte block holding 1000 pointers. How queue slots are created has not been decoded.
- Connections (
0x1400a7aa0):- Type 1 WEB =
db_web, 2 AUTH =db_account, 3 GAME =db_game, 4 GAME LOG =db_game_log. - A timeout of 0 becomes 500.
- A string longer than 255 characters gives
Error … String Max.!. - AUTH uses the WEB timeout (+0x134); this is an original bug.
- Type 1 WEB =
- Startup (
0x140042cf0): opens AUTH and GAME connections, thenup_read_world_serverreads the world list. - Connect failure (
0x1400a80c0):DB Connect failed\n Connection String = %sis appended toError_ADO DB Connect.Log. The original writes the password verbatim. - Procedure error:
ADO_ON_ERROR : [ %s ] Proc [ %s ]with source file and line (catch funclets,dbreq_methods.tsv). - Single instance: mutex
Global\RustyHearts_DBC. - Startup modes: a numeric
argv[1]selects agent mode (total_option.xml<db><game>); otherwiseargv[1]is the path of the per-server XML (docs/CONFIG.md).
Win32 → Linux substitutions
| Original (address) | Win32 / library | Nearest Linux equivalent | Notes |
|---|---|---|---|
| Single instance | CreateMutexW("Global\\RustyHearts_DBC") |
flock(LOCK_EX|LOCK_NB) on $TMPDIR/RustyHearts_DBC.lock |
Lock file instead of a kernel object |
DB access (0x1400a7aa0, 0x1400a80c0, DBReq*) |
ADO _Connection / ADODB.Command over SQLOLEDB, CoInitialize, _com_issue_errorex |
ODBC: unixODBC + Microsoft ODBC Driver 18, or FreeTDS libtdsodbc |
Call as {? = call dbo.proc(?,…)} with positional parameters (ADO uses named ones); GUIDs in SQL Server byte order. ODBC 18 needs TrustServerCertificate=yes, because SQLOLEDB never validated TLS. FreeTDS needs Port= (not host,port) and TDS_Version=7.4 |
| ADO connection string | Provider=SQLOLEDB;Data Source;Initial Catalog;User ID;Password |
Driver={…};Server;Database;UID;PWD |
Keys map 1:1. Treating the XML timeout as milliseconds (rounded to seconds) is an inference |
| Transport | ProudNet CNetServer over Winsock2/MSWSOCK/IOCP |
BSD sockets / epoll | ProudNet internal net version is 196978; 1.8 is 197144, so the protocols are incompatible. Interop would need a reimplementation of the 2013 protocol (third-party/README.md) |
Write queue (0x140144730) |
CRITICAL_SECTION + semaphore (ReleaseSemaphore) |
std::mutex + std::condition_variable |
— |
| Threads | _beginthreadex |
std::thread |
— |
XML (MagiServerOption, MagiCustomOption) |
TinyXML 2.5.x | pugixml with parse_fragment (the files have several top-level elements) |
— |
| Console / stop | wprintf, console menu |
stdout, SIGINT/SIGTERM; ignore SIGPIPE |
— |
timeGetTime |
WINMM | std::chrono::steady_clock |
— |
| Crash reporting | BugTrap, MiniDumpWriteDump |
core dumps | — |
Prior validation of the DB mapping (2026-10-14)
Run with the removed port against copies of the shipped .mdf files, on
SQL Server 2022 through FreeTDS 1.5.19:
up_read_world_serverreturned 1 row.REQ_READ_ALL_GUILD(10101)returned the guild row.REQ_UPDATE_CHARACTER_HEARTS(guid, n)updatedCharacterTable.Hearts(then reverted).REQ_INVEN_SLOTfailed with "Could not find stored procedure", confirming that procedure is missing from the shipped schema.
This validates the mapping and the parameter types against the schema. It says nothing about the original binary's runtime behaviour.
Unknown / not recovered
- RMI argument marshalling (ProudNet) and the per-
DBReqpost-processing of recordsets (e.g.FIRST_CHARACTERin0x1400acbb0). - 52 RMIs without a procedure mapping, mostly log, logout and auth.
- Some mappings may be wrong: those found two call levels deep, e.g.
REQ_CHARACTER_READ_ALL_DATA→up_read_Inquiry_character. - Which connection each queue/
DBRequses: inferred from the database that holds the procedure. - Queue-slot creation; agent-mode address/port (pushed by the Agent over
RMI); the
guildlobby,channelandOptionelements.