Skip to content

Latest commit

 

History

History
181 lines (129 loc) · 12.9 KB

getting_started.md

File metadata and controls

181 lines (129 loc) · 12.9 KB

Getting Started

See Dependencies first.

  1. Clone the repository.

  2. Place the original game executable and its DLLs in orig/<VERSION>/. The path inside orig matches the version ID in configure.py.

    • orig/BW1W100/runblack-decrypted.exe — Windows v1.0 (decrypted)
    • orig/BW1W110/runblack-decrypted.exe — Windows v1.1 (decrypted)
    • orig/BW1W120/runblack-decrypted.exe — Windows v1.2 (decrypted)
    • orig/BW1W130/runblack-decrypted.exe — Windows v1.3 (decrypted)
    • orig/BW1WCI/CreatureIsle-decrypted.exe — Windows Creature Isle (decrypted)

    The Windows builds also need the four DLLs shipped alongside the exe, in the same directory: LHaudiodllR.dll, LHLogR.dll, LHMultiplayerR.dll, LHDialogLib.dll (copy these from your own install — the build checks all five files' hashes against config/<VERSION>/build.sha1 and fails at the split step with a clear "not found" message naming whichever one is missing).

    The Windows builds expect a decrypted exe. The original retail discs ship the executable wrapped in SafeDisc 2 / Macrovision protection. Decrypting it is currently undocumented tribal knowledge — if you don't already have a decrypted copy, ask in the project's Discord/issue tracker rather than guessing at a tool name.

  3. Place the MSVC 6.0 static CRT libraries (and Visual Studio .NET 2002's libcmt.lib) and the DirectX 7.0 SDK headers under orig/ (they are not committed and not downloaded).

    BW1W120 was built with VC6 SP5; BW1W100/BW1W110 with VC6 SP4 (confirmed via Rich header build-number comparison — see configure.py). BW1W130 and BW1WCI use BW1W120's libraries. configure.py picks the right package automatically based on --version, so both sets need to be present side by side if you plan to work on more than one version.

    MSVC 6.0 SP5 static libs → orig/libs/msvc6.5/ (BW1W120)

    Needed: libcmt.lib and libcpmt.lib. If you have a Visual C++ 6.0 (SP5) install, copy them straight from VC98\Lib. Otherwise pull them from a plain ISO of the SP5 disc on the Internet Archive, item MSDN_VisualStudio_6.0_SP5_MASM_6.11_Visual_C_1.2 (VS60SP5_EN, byte-verified identical to the genuine SP5 CD):

    1. Download MSDN_VisualStudio_6.0_SP5_MASM_6.11_Visual_C++_1.2.iso from that item. It's a plain ISO9660 image — no proprietary disc-image format, no conversion step.
    2. Extract ENGLISH/VS60SP5/VC98/LIB/LIBCMT.LIB and LIBCPMT.LIB with 7z e <iso> "*LIBCMT.LIB" "*LIBCPMT.LIB" -r, or mount the ISO directly (mount -o loop on Linux; double-click on Windows/macOS).
    3. Copy the two files to orig/libs/msvc6.5/libcmt.lib and orig/libs/msvc6.5/libcpmt.lib. Filenames are matched case-insensitively; lowercase is fine.

    MSVC 6.0 SP4 libcmt.lib → orig/libs/msvc6.4/ (BW1W100/BW1W110)

    From the Internet Archive item msdn-disc0034-january-2001-x06-11596 (a plain ISO, no disc-image conversion needed):

    1. Download 1_V60SP4_DE.iso.
    2. Extract GERMAN/VS60SP4/VC98/LIB/LIBCMT.LIB with 7z e <iso> "*LIBCMT.LIB" -r, or mount directly.
    3. Copy it to orig/libs/msvc6.4/libcmt.lib.

    MSVC 6.0 RTM libcpmt.lib and libcimt.lib → orig/libs/msvc6.0/ (libcpmt.lib for BW1W100/BW1W110, libcimt.lib for BW1W110/BW1W120/BW1W130)

    SP4 never shipped its own libcpmt.lib — checked three independent SP4 discs, none include it, since Microsoft only redistributes files that changed for a given service pack. The original 1998 RTM copy is the right one (a real SP4-era machine would still have it, untouched, in VC98\Lib). It's byte-different from SP5's, so don't substitute that one.

    libcimt.lib — the pre-standard iostreams (ios, streambuf, filebuf, fstream, istream, ostream), which the game uses alongside the standard library in libcpmt.lib — is a stronger case of the same thing: no service pack ever replaced it. The SP5 package shows this plainly, every library in it dated 2000-08 except libcimt.lib, still the 1998-06-17 original. So RTM's copy is what 1.10 and 1.20 both link, and it is the only one that matches: a later rebuild (Feb 2004, SP6-era) exists in the wild whose _ios.obj differs — its ios constructors are 0x64/0x6F/0x4D/0x51 bytes against RTM's 0x5E/0x69/0x48/0x4A, and it adds an __InitLock class and a .CRT$XCL static initializer that appear nowhere in either game binary. If your copy is dated 2004, it is the wrong one.

    Both come off the same disc, from the Internet Archive item visualstudiov60enterpriseedition_199807:

    1. Download Disc 1 (...(X03-78941)(Microsoft Corporation)(August 1998).iso, a plain ISO).
    2. Extract VC98/LIB/LIBCPMT.LIB and VC98/LIB/LIBCIMT.LIB with 7z e <iso> "*LIBCPMT.LIB" "*LIBCIMT.LIB" -r, or mount directly.
    3. Copy them to orig/libs/msvc6.0/libcpmt.lib and orig/libs/msvc6.0/libcimt.lib.

    Visual Studio .NET 2002 libcmt.lib → orig/libs/msvc7.0/ (BW1W120/BW1W130)

    LHLogR.dll was rebuilt for 1.20 with Visual Studio .NET 2002 and links that version's static CRT (its Rich header lists Utc13_C 9466 and masm 9162 objects); 1.30 ships the same DLL. If you have a Visual Studio .NET 2002 install, copy Vc7\lib\libcmt.lib. Otherwise take it from the Internet Archive item X08-65717 (Microsoft Visual Studio .NET 2002 Professional Edition, Disc 1, RTM, January 2002):

    1. Download 2002-02_X08-65717_X08-65353_VSPROD1.7z (311497891 bytes, md5 c1763dcd16f5e8b03cf3ac844e17175f) and extract image/VSPROD1.mdf from it with 7z e 2002-02_X08-65717_X08-65353_VSPROD1.7z image/VSPROD1.mdf.

    2. The image is an Alcohol 120% MDF that 7z cannot read as is: raw 2448-byte sectors (2352 bytes of Mode 1 data plus 96 of subchannel), each holding its 2048 bytes of user data at offset 16. Convert it to a plain ISO:

      python3 -c "import sys;f=open(sys.argv[1],'rb');o=open(sys.argv[2],'wb');[o.write(s[16:2064]) for s in iter(lambda:f.read(2448),b'')]" VSPROD1.mdf VSPROD1.iso
      
    3. Extract Program Files/Microsoft Visual Studio .NET/Vc7/lib/libcmt.lib with 7z e VSPROD1.iso "Program Files/Microsoft Visual Studio .NET/Vc7/lib/libcmt.lib", or mount the ISO directly.

    4. Copy it to orig/libs/msvc7.0/libcmt.lib. Its SHA-1 is c4c2b278697318c7289f50a73faf7cd47d01814e.

    DirectX 7.0 DDK → orig/directx7.0/

    From the Internet Archive item dx7ddk:

    1. Download dx7ddk.exe (a self-extracting installer).
    2. Extract it with 7-Zip (7z x dx7ddk.exe), or run it on Windows to install.
    3. Copy both the include directory (holding ddraw.h, d3d.h, dinput.h, dsound.h, …) and the lib directory (holding ddraw.lib, dinput.lib, dsound.lib, dxguid.lib, …) to orig/directx7.0/include/ and orig/directx7.0/lib/.

    Intel Approximate Math Library (AM Library) → orig/libs/amaths-2.0/

    Needed: amaths.lib and its header AMaths.h. The game links Intel's AM Library verbatim (SSE/SSE2 sin/cos/sincos/tan/atan/exp/log/pow), Release 2.0 (Oct/Nov 2000). Intel's own download location is long gone; pull the package from the Wayback Machine:

    1. Fetch AMaths.zip from the Internet Archive capture of Intel's old Pentium III devtools page: https://web.archive.org/web/20060218064543id_/http://www.intel.com/design/pentiumiii/devtools/AMaths.zip (the id_ suffix returns the raw file instead of a replayed page).
    2. Unzip it. It contains AMaths.lib, the header AMaths.h, AMaths.c (source, for reference), AMaths.pdf (documentation), MathTest.exe/.cpp (Intel's test harness), and license files.
    3. Copy AMaths.lib to orig/libs/amaths-2.0/amaths.lib. As with the MSVC libs, the filename must be lowercase to match the config key.
    4. Copy AMaths.h to orig/libs/amaths-2.0/AMaths.h, keeping its name as it is: LH3DP3.cpp includes it.

    Intel C++ 5.0 libircmt.lib → orig/libs/icc-5.0.115/ (BW1W110/BW1W120/BW1W130)

    Needed: libircmt.lib, the multithreaded Intel C++ runtime. One member is linked, cpu_disp_mt.obj — the CPU dispatch code (___intel_cpu_indicator and its init) that LH3DP3.cpp, the one translation unit built with icl, drags in.

    Both versions link the 5.0.1 beta, build 010214Z, which has not turned up anywhere. The nearest release, 5.0.115 (build 010525Z), is close enough to use: its cpu_disp_mt.obj has the same section layout and differs only in ten code bytes across nine sites (a stack adjust, and the [ebp-4]/[ebp-8] locals swapped) plus the compiler banner in the .drectve. tools/post_link_patch.py rewrites both, so the image still hashes clean — delete those two patches if a 010214Z copy ever surfaces.

    The compiler is downloaded automatically (see below) and ships the libraries with it, so the simplest source is build/compilers/ICC/5.0.1/lib/:

    1. Run ninja once so the compiler is fetched.
    2. Copy build/compilers/ICC/5.0.1/lib/libircmt.lib to orig/libs/icc-5.0.115/libircmt.lib, lowercase as above.

    Failing that, it can be extracted from Intel C++ Compiler 5.0.115 install media, where the libraries sit in the InstallShield cabinet compiler/icl/data1.cab:

    1. unshield -g "Compiler32 Lib files (shared)" -d <out> x <media>/compiler/icl/data1.cab
    2. That yields libirc.lib, libircmt.lib, libm.lib, libmmd.lib, libmmt.lib and svml_disp.lib. Only libircmt.lib is needed; it should hold psa_mt, dispfail_mt, cpu_disp_mt, xmm_malloc_mt, cpcheck_mt and pgopti_mt.
    3. Copy it to orig/libs/icc-5.0.115/libircmt.lib, lowercase as above.

    QSound QMixer 5.00 SDK → orig/libs/qmixer-5.00/

    Needed: the header Qmixer.h, which LHaudiodllR.dll's sources include for the QMixer.dll they import. The game ships QMixer.dll 5.00, and the only 5.00 SDK left is QSound's own download, captured by the Wayback Machine:

    1. Fetch qm500sdk.exe: https://web.archive.org/web/20010429011901id_/http://www.qsound.com:80/gamedev/qmdx-files/qm500sdk.exe (SHA-1 2dcd6c29ba0a11ee9a9558b2c96a49b7c89f5dae).
    2. It is an InstallShield 3 installer: run it on Windows, or under Wine, and take the files from the qmixer directory it installs. 7z x only unpacks the outer layer, leaving the files compressed inside qmsdk.z.
    3. Copy Qmixer.h to orig/libs/qmixer-5.00/Qmixer.h, keeping its name as it is: LHAudioPCH.h includes it. Its SHA-1 is dcd23c08d5ee425aa51e67aa6a43f19871e5d707.
    4. Optionally copy QMixer.lib alongside it (SHA-1 6174df13dd1d181080848047151e3a955da26754). The build does not link it, since imports come from the split .idata, and it is not the exact library Lionhead linked. That one also exported QSWaveMixGetWaveLength, like the shipped QMixer.dll, so 21 of the DLL's 32 QMixer import hints are one higher than this library's.
  4. Run configure.py with the version you want to work on:

    python configure.py --version BW1W100

    Pass --version BW1W100, --version BW1W110, --version BW1W130 or --version BW1WCI for the other targets. Default is BW1W120.

  5. Build:

    ninja

    The first build downloads dtk, lld-link, and the MSVC 6.0 compilers, then runs dtk pe split (or dtk pef split for Mac) to produce per-function .o files, links them, post-patches the result, and verifies the SHA-1 against config/<VERSION>/build.sha1.

If the SHA-1 check passes, the rebuilt binary is byte-identical to the original.

Configuration files

Each version lives under config/<VERSION>/:

  • config.yml — project config, points at the original binary and the splits.txt / symbols.txt files for that version
  • splits.txt — assigns address ranges to source files (see splits.txt)
  • symbols.txt — names and types every symbol in the binary (see symbols.txt)
  • build.sha1 — expected SHA-1 of the original (and, eventually, the rebuilt) binary

Decompilation workflow

  1. Pick a function from the disassembly that you want to match.
  2. Add a splits.txt entry assigning its address range to a source file.
  3. Add a configure.py Object(NonMatching, "...") entry under config.libs for that source file.
  4. Write the C/C++ source in src/ until objdiff shows the compiled .o matches the disassembled .o.
  5. Flip the entry to Object(Matching, "...") and rebuild — the function is now linked from your source instead of the auto-generated split.

Run python configure.py progress (or ninja progress) to print the current code/data match percentage.