Android port - #6
Open
timskap wants to merge 19 commits into
Open
Conversation
Builds the 2003 engine sources for Android with the NDK for arm64-v8a,
armeabi-v7a and x86_64, plus a headless host target for debugging.
Misc, FileIO, Script (Lua 4.0) and MiscDll are ported and verified on
device against real game data: .res packages open and read, the chunk
serialiser round-trips, and the game's own Lua sources execute. The
renderer, audio and the game layer (Main) are not ported yet; the
roadmap is in android/docs/PORTING.md.
The historical sources under Soft/ are not modified. Instead
android/tools/prepare_sources.py stages the modules being built into
android/gen/ and applies 45 named, individually documented rewrites
(include paths, x86 inline assembly, MSVC-only C++, 64-bit fixes);
--report prints the complete list, which is the port's diff against
2003. Everything else is new code under android/compat (a Win32 ->
POSIX shim) and android/platform (NativeActivity entry point, GLES
boot console, data mounting, boot harness).
Three original bugs only surface on a 64-bit target and are fixed by
those rewrites:
* CBufferedStream::LoadBufferForced shifted pointers by
nBufferStart - nPos computed in unsigned int, relying on 32-bit
pointer wraparound; on arm64 it moves the pointer 4GB away, which
made every .res package segfault.
* CBufferedStream::SetNewBufferSize truncated a pointer difference
to int.
* CRandomGenerator::FillRandRsl seeded ISAAC by walking C:\ for a
random file and looped forever when it found none.
Float2Int is also worth noting: the x87 original rounds rather than
truncates, so it maps to lrintf, not a cast.
Verified on a Galaxy Z Fold7 (Android 16, Adreno 830), on the emulator,
and headlessly on macOS.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Wide strings. The engine's wide strings are UTF-16 (2-byte wchar_t on Win32) and the on-disk format depends on that width; Android's wchar_t is 4 bytes. The staging tool now rewrites the sources to char16_t / std::u16string, WCHAR is char16_t in the compat windows.h, and a new compat/src/wide_char.cpp supplies the char16_t CRT forms together with generated windows-1251/1252 tables behind MultiByteToWideChar and WideCharToMultiByte (the original relied on CP_ACP of a Russian machine). DBFormat. The 130 game-object record classes plus the ADOFake database stub now build and run. Getting there needed a set of mechanical passes in prepare_sources.py for MSVC 7 leniencies that recur across the tree and will cover Main as well: forward-declared enums given ': int', typename on dependent iterator types, `if ( CDynamicCast<T> p( x ) )` condition declarations rewritten to '= x' with balanced-paren parsing, typeid on incomplete types in CClassFactory (now keyed by typeid(T*)), and the CPtr<T> == T* overload ambiguity (member operator templated on the pointee). Serialiser. CStructureSaver stored 4 bytes of an object's *address* as its on-disk reference ID and mapped them back on load; on 64-bit that truncated on write and left half a void* unwritten on read, so every reference resolved to nothing. References are now uint32 throughout and stored objects are numbered densely on write. Format unchanged. Result. game.db parses and all tables register -- but every game.db in the repository was written by a later build than this January 2003 source: tables are heap objects of an unregistered class 0xA1843130 with a column layout this source has no schema for. The serialiser reports unknown types precisely and the harness shows this as a warning, not a failure. Documented in docs/PORTING.md with the two ways forward. Verified: 22/22 host, 21/21 on the Galaxy Z Fold7 (arm64-v8a). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Builds libpng 1.0.9 from the tree (NDK zlib, x86 assembler paths off) and the engine's Image module: BMP/TGA/PNG loaders and the MMP mipmapped texture container. ImagePack.cpp -- the DXT encoder on a proprietary s3tc.h, used only by the TexConv tool -- stays out of the runtime build. Adds a software DXT1/3/5 decoder under platform/ for GPUs without GL_EXT_texture_compression_s3tc and for verification: the harness loads real textures through NImage::LoadImageMMP, decodes the top mip and compares its mean colour to the dwAverageColor the 2003 tools stored in the header. They agree to within 1/255 on every texture tried. Verified: 29/29 host, 28/28 on the Galaxy Z Fold7. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Starts compiling the engine+game module. The initial survey produced 26,478 errors across all 271 files; one root cause -- Main/Time.h shadowing the system <time.h> when Main/ is on the include path -- was 72% of that. Main/ is no longer an -I directory (engine sources include siblings relative to themselves, which needs none). The rest was recurring MSVC 7 leniencies, now handled once in prepare_sources.py so they will not come back: `typename` on any qualified iterator inside a template body (242 sites), single-argument insert(end()) (34), unqualified member-function names passed as pointer-to-member (11), dependent-base member access in class templates (the CObjectBase macros now use this->; using-declarations elsewhere), typeid on incomplete types in the event registry (keyed by T* like the factory), and unions with non-trivial members (SMove). -fno-operator- names because the engine has variables named `or`. Also: Input is staged for its headers (Input.h is a clean, DirectInput- free seam for the touch layer; Bind.h/Bind.cpp are portable), and the char16_t CRT gains wcstol/_wtol. Main is an OBJECT library behind -DA5_BUILD_MAIN=ON; nothing links it yet. 87 files still fail: LifeStudio:HEAD SDK headers (proprietary, absent), a handful of dependent-base and conversion one-offs, and the D3D-facing files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Main.vcproj is now the authority on which files build (the directory holds two sources MSVC never compiled: Interface.cpp and iPopupMenu.cpp reference types that no longer exist). prepare_sources.py emits a SOURCES.cmake per module from the vcproj. The MMX inline-assembly skinning routines in GCombiner.cpp -- the only path for skinned normals and tangents -- are replaced by the same fixed- point arithmetic in portable C++ (16-bit saturating adds, same shifts, same normalisation table). A standalone check against a float rotate+normalise gives mean error 0.0042, max 0.0096; Nival's own commented-out check tolerated 0.02. LifeStudio:HEAD (facial animation, proprietary, absent from the release) gets a stub under compat/include/thirdparty-stubs: dialogue heads render in their neutral pose instead of animating, and the stub says so once in the log. More MSVC 7 leniencies handled once for the whole tree: converting constructors between smart-pointer kinds (CObj<Derived> -> CPtr<Base>), CPtr == 0, address-of-temporary hoisted into locals, unqualified member-function names as pointer-to-member arguments, private nested types named from outside the class, non-const reference parameters bound to temporaries, implicit int, and a friend declaration relied on for ordinary lookup. _findfirst/_findnext/_findclose and the VK_ key codes join the compat layer. Remaining: FMOD (SoundFormat/SoundEffect/Sound), a few mixed-type min/max calls, swscanf on char16_t, CDGPtr conversions, and a handful of one-offs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…end remains
The last x86 inline assembly is gone. Four MMX sites replaced with the
same arithmetic in C++: the AABB accumulator in Bound.h (state moves from
mm4-mm7 to a thread-local), the bilinear resample in SWTexture.cpp and
the 2D 'over' blend in 2DSceneSW.cpp (Nival left the C++ in a comment
beside the asm), and the particle colour modulate in GSceneParticles.h,
which is transcribed lane by lane and verified bit-exact against an
emulation of the instruction sequence over 800k random inputs.
FMOD: FModSound/FMsound.h is staged as the audio seam and
platform/audio_null.cpp implements the whole NFMSound interface silently,
so Sound.cpp/SoundEffect.cpp/SoundFormat.cpp build.
Remaining MSVC 7 leniencies handled once for the tree: min/max as macros
(the engine never set NOMINMAX and mixes types in ways std::min rejects),
insert(end()) through dotted/arrow container expressions (41 sites),
converting assignment on CDGPtr, and a handful of one-offs -- swscanf on
char16_t rewritten to explicit strtol/suffix parsing, `struct _stat` to
POSIX stat, wcsftime narrowed, `{ (0,0) }` as an aggregate initialiser,
implicit-int declarations, GetCursorPos/SPI_GETMOUSE to a compat pointer
hook. The member-argument pass no longer touches free functions. BMP
headers, GetDoubleClickTime and _findfirst join the compat layer.
Both toolchains agree: 264/269 on aarch64-linux-android and on the
macOS host. The five files that do not compile are exactly the Direct3D
9 backend (Gfx.cpp, GfxBuffers.cpp, GfxRender.cpp, GfxEffects.cpp,
GfxShadersDescr.h): ~6,500 lines using ~40 IDirect3DDevice9 methods,
which docs/PORTING.md now lists as the scope of the next chapter.
Boot harness: 29/29 host, 28/28 on the Galaxy Z Fold7.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
docs/RENDERER.md writes down what the five remaining D3D9 files must provide behind the engine's NGfx interface: the CRenderContext state model and its Apply<> translation tables, the atlas sub-allocation the 2D/UI path depends on, the LRU vertex-buffer cache with its base-vertex emulation, the three vertex formats byte by byte, the VS constant register map, all 77 vertex and 78 pixel shaders by name (their assembly source is embedded in the bytecode's DBUG chunks), the ps1.x features that need hand-porting, the screen-sized register bank, render-target and cube-map consumers, and a suggested order of work. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tools/extract_shaders.py pulls the vs.1.1 / ps.1.1 / ps.1.4 assembly out of the DBUG chunks in GfxShaders.cpp's bytecode -- 77 vertex and 78 pixel shaders as text under shaders/d3d/, plus an index recording which pixel shaders enable alpha test (13) and which have a ps.1.4 variant that differs from the ps.1.1 one (34; the difference is that ps.1.4 spells projective reads out as t0_dw, so those variants are the ones translated). tools/d3dasm2glsl.py is a translator, not a hand port: the assembly is a small register language and the look depends on ps.1.x semantics that are easy to get subtly wrong by hand -- the _x2/_x4/_bx2/_sat modifiers, the [-1,1] register range after every instruction, co-issued colour and alpha ops reading their sources simultaneously, texm3x2/texm3x3vspec and texreg2ar dependent reads. Encoding those once gives every shader the same treatment and lets the GLSL be regenerated as the rules improve. The vertex template also carries the D3D->GL clip fixups (z range, y flip for render-to-texture, half-pixel offset) and the D3DCOLOR .zyxw attribute swizzle. Samplers are typed per stage by a cube mask supplied by the caller, since ps.1.x `tex` samples whatever is bound. shaders/glsl/ holds the output; tools/validate_glsl.sh checks all 155 with the NDK's glslc (155/155 pass as ES 3.10 layout-annotated copies; the on-device compile is the real test and comes with the backend). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…evice compat/d3d9gles/ implements the slice of Direct3D 9 the engine's renderer uses (Gfx.cpp, GfxBuffers.cpp, GfxRender.cpp: ~40 IDirect3DDevice9 methods and ten resource types) on OpenGL ES 3.0, so those 3,800 lines of Nival's renderer -- LRU-cached vertex buffers, the UI texture atlas, the render-target pool, index batching, redundant-state filtering -- can be kept as they are instead of rewritten. Conventions: framebuffer memory is always in D3D layout (row 0 = top); every draw goes to an FBO with clip-space y flipped and culling inverted to match, so render targets the engine samples back have the layout its projective UVs expect, and Present blits the virtual back buffer to the window with a vertical flip and a letterbox. The back buffer is an FBO of the size the engine asks for (4:3 modes are enumerated; the game's UI is fixed-pixel). Textures keep a CPU shadow so LockRect behaves like the managed pool; DXT goes up as S3TC where the GPU has it, decoded otherwise; A8R8G8B8/1555/4444 are converted to GL layouts on upload. Vertex and index buffers keep a shadow with dirty ranges (MarkDirty, a hint the engine's sub-allocator will supply), uploaded on Unlock. Shaders: CreateVertexShader/CreatePixelShader receive the engine's own bytecode, find the assembly text in its DBUG chunk and look the GLSL up by FNV-1a hash in the generated table; programs are cached per (vs, ps, cube-mask) with per-register uniform locations, since the GLSL compiler trims constant arrays to the highest index a shader uses. platform/d3d_selftest.cpp drives the shim from the boot harness on the live context: creates the device, compiles all 155 shaders from GfxShaders.cpp's real bytecode tables, links 154 (vs, ps) programs and draws with each, and checks pixels through GetFrontBufferData and a render-target LockRect. On the Galaxy Z Fold7 (Adreno 830, S3TC present): 154/154 programs, D3D row order confirmed, RTT and texture upload correct. 36/36 harness checks pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…me loop, RTTI/LONG fixes - platform/db_retail.cpp replaces ADOFake: reads the retail game.db generic column-store tables (class 0xA1843130) and runs the record classes' own ADO-style Import() over them; Complete/game.db loads 130/130 tables, 222k records in ~0.5 s on device (A5_DB_DUMP=1 lists columns) - prepare_sources: rules for the D3D9 backend files (269/269 compile), MarkDirty hints, GfxBuffers/GfxRender/GfxEffects fixes, link-time Interpolate/At rules, texture-cache/MakeTexture diagnostics - CDynamicCast and (CObjectBase*)pContext casts go through a5_cast_opaque<> (compat/src/rtti_compat.cpp): MSVC's RTTI tolerated dynamic_cast from a reinterpreted pointer, the Itanium ABI does not - compat: LONG is 32-bit (RECT/POINT must match CTRect<int>/CTPoint<int>), pointer-position API, dinput.h stub - platform: game_entry (WinMain as init/step/shutdown), input_android (NInput over Android keys/touch), android_main game loop with hooks and periodic step/present diagnostics, audio_null in the game build - CMake: a5main static lib, A5_BUILD_MAIN; push_data pushes Complete/game.db - docs: PORTING.md/README status -- the game loop runs on the Z Fold7 and presents its first frames Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…k, console echo, diagnostics
- d3d9gles: cull mapping was mirrored the wrong way (D3D winds in projected
space, y up; our y-flipped FBO mirrors it): D3DCULL_CW -> glFrontFace(GL_CW).
Every 2D quad had been culled. Frame stats (A5D3DGetFrameStats), per-draw
trace + back-buffer histogram (A5_D3D_TRACE), state overrides (A5_D3D_FORCE),
LockRect diagnostics
- CMake: engine static libs linked whole-archive -- START_REGISTER /
REGISTER_SAVELOAD_CLASS objects that nothing references were being dropped
('mainmenu' unknown)
- game_entry: no start.cfg -> straight to the main menu; step logs stats
- MiscDll rule: console lines echoed to logcat as UTF-8 ('console: ...';
'%S' meant UTF-16 on MSVC), a5_u16_to_utf8 in compat
- android_main: <external files>/env.txt sets debug environment variables
- db_retail: A5_DB_DUMP_ROWS=<table>; harness: main-menu UI container check
(warns: retail UI layout differs from this source's iMainMenu.cpp)
- docs: PORTING.md/README -- main menu (logo, version, localised text, cursor)
renders on the Z Fold7; next steps listed
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…g; menus navigable by touch - BasicFactory: pointer-typed table lookup compared type_info by identity; the Itanium ABI gives typeid(T*) for an incomplete T internal linkage (name starts with '*'), so every DB cross-reference imported from a file that only forward-declares the target class was null (no objects on any map). Compare mangled names. - Particle effects: SParticle file records hold 32-bit offsets in pointer fields; decode into native records instead of reinterpreting (crash on arm64) - Retail-data adapters (rules): main-menu buttons and side-menu NEXT/BACK laid out on the templates' text lines; 'clientview' -> 'view'; cursor UITexture ids aliased by name (db_retail UserName index); VapourSwitchTime 0 no longer divides by zero; VapourDensity read as per-100-units - SWTexture: DXT1/3/5 MMPs decoded in software (terrain painting) - Input: absolute pointer applied before the interface turns a button message into a positioned event -- one tap clicks; c:\a5\ paths mapped to the data root (autoexec now loads input.cfg/config.cfg) - Harness: object-chain check for the main-menu template; d3d9gles: A5_D3D_SKIP, A5_D3D_TRACE_CONST; db_retail: A5_DB_DUMP_ROWS multi-line strings, A5_DB_TRACE_REF Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s count as existing The retail data ships Geometries/ and AIGeometries/ as loose files (7,790 of them), not as .res packages. This source's release build only asked the package whether a file exists (the loose-file check in CResourceFileOpener::DoesExist is under _MAPEDIT), so CGameView:: AddModelPart / CreateOccluder / GBuilding skipped every model part and no object was ever submitted to the renderer: the main menu drew 15 terrain patches, fog and particles over an empty platform (frame trace showed zero object draws; the DB chain for all 18 template elements resolved). prepare_sources rule set 17: DoesExist falls back to a5_stat_exists on the same "<Res>\<id>" name CFileResource opens. On the Z Fold7 the menu now shows the station platform, locomotive and tender, buildings, posts, rubble and fire. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lias, build-root override - d3d9gles: A5D3DDrawsByShader (A5_D3D_SHADERS=1 prints the top vs+ps pairs every 5 s next to the game stats line) - boot_harness: object-chain check covers the allies hero screen (2999) too, reports skinned models - db_retail: cursor alias 492 -> "Normal Pen" (options / chapter map cursor) - prepare_sources: A5_DEBUG_SKIN diagnostic in CGameView::CreateSkin - scripts: A5_BUILD_ROOT / A5_CMAKE_EXTRA so two sessions can build side by side (build-*/ ignored) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sic plays on device - platform/audio_android.cpp replaces the null back end: 64 sample voices + streams mixed in float at the device rate (linear resampling) on a mixer thread that writes to AAudio with blocking writes; AAudio is dlopen'ed so minSdk stays 24. Semantics follow the FMOD wrapper (handle drop stops the voice, clip-space 3D attenuation with FMOD's log rolloff, 0..255 volumes, sample fades in source samples, stream fade-out in seconds). One deliberate divergence: PlayStream() shares a stream already playing the same file -- every pushed menu interface creates its own sound scene and would otherwise layer another copy of the ambient track. - platform/audio_decode.cpp: WAV (PCM, Microsoft ADPCM, IMA ADPCM) and Ogg Vorbis via vorbisfile over memory. The retail assets were encoded with libVorbis 1.0 beta3/RC1 and use floor type 0, which stb_vorbis cannot decode (6901 of 6910 samples, all 30 music tracks) -- so libogg 1.3.5 + libvorbis 1.3.7 are vendored under thirdparty/ and the stb copy goes. - tools/audio_decode_test.cpp (host: silentstorm_audiotest): all 1584 WAVs are bit-exact against ffmpeg (MS ADPCM prediction must truncate toward zero, not shift), all 6940 Ogg files decode to their declared length. - android_main: pause/resume the mixer with the activity, audio stats every 5 s (voices, streams, output peak, underruns); game_entry: A5_START_CFG; push_data.sh --full also pushes Versions/Current/res/Music. - Verified on the Z Fold7: Res\Music\Mainmenu.wav streams at 48 kHz, 0 underruns, peak 0.26. 3D voices need a mission (currently crashes in NAI::CPathNetwork::GetCP on unit creation, not audio). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…when idle, PORTING.md status - iHeroMenu adapter: BACK / CUSTOM CHARACTER / NEXT along the template's single 'line' (retail container 354); MakeLineControl accepts 'line' - input_android: an empty queue yields a CT_TIME message like DirectInput did, so the game clock advances without input (scene animation ran only while the cursor moved) - PORTING.md: status as of 2026-08-19, three engine-wide traps documented Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… errors logged with their Lua stack - platform/script_prelude.cpp, run after the auto-load scripts (wMain.cpp rule): the retail scripts call ~100 functions this snapshot's ScriptFunctions.cpp lacks (list from diffing every call in game.db Scripts and scripts/*.l against the registered names); in Lua 4 one nil call aborts the whole script. WaitForObject/Random/TableGetSize/Get+SetGlobalGameVar in Lua on the existing API, the rest no-ops that report themselves once - Script/ldo.cpp rule: script errors go to the log with source/function/line Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…Lua has none); harness loads and exercises it Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Silent Storm — Android port
An Android build of the Silent Storm engine, made from the 2003 source release in
this repository (
Soft/Andy/Jan03/a5dll). The original targets Win32, DirectX 8/9,MSVC .NET 2003 and STLport; this directory builds the same code with the Android
NDK (clang/libc++) for
arm64-v8a,armeabi-v7aandx86_64.Status: the whole engine builds, links and runs on Android; the game reaches
its first screen. File I/O, the chunk serialiser, the
.respackage reader,the object model, the Lua 4 VM, texture loading and the retail
game.dbimporter are verified against real game data on device.
Main— the renderer,scene, AI, UI and game logic, 154k lines — compiles for arm64 in all 269 of its
files; the Direct3D 9 backend runs on a D3D9-on-GLES 3.0 implementation
(
compat/d3d9gles/, all 155 engine shaders translated). On a Galaxy Z Fold7 themain menu comes up — logo, version string, localised text, cursor — at
~120 fps into a 1024×768 virtual back buffer; the menu's 3D scene and touch
navigation are the next steps. See docs/PORTING.md for the state of each
piece and what comes next.
The app currently boots, mounts your game data, exercises those subsystems and
shows the result on screen through GLES 2.0:
Verified on a Galaxy Z Fold7 (SM-F966B, Android 16, Adreno 830), on the Android
emulator, and headlessly on macOS.
Quick start
Requirements: Android SDK with build-tools and an NDK (r27 and r29 are both
tested), CMake 3.22+, Ninja, Python 3, a JDK for
apksigner. No Gradle needed —though
android/is also a valid Gradle project if you prefer Android Studio.To run the engine checks on your development machine instead of a device:
See docs/RUNNING.md for where the game data has to live.
How the port is organised
The original sources are never modified.
tools/prepare_sources.pycopies themodules it builds into
gen/and applies a set of named, individually documentedrewrites on the way through — include paths, x86 assembly, MSVC-only C++, and a
handful of genuine 64-bit bugs. Run it with
--reportto see every change itmakes:
That list is the port's diff against 2003. Everything else is new code in
compat/andplatform/.What is ported
MiscFileIO.respackagesScriptMiscDllDBFormatgame.dbschema (130 record classes)platform/db_retail.cppimports the shipped (retail-format)game.db: 130/130 tables, 222k recordsImage+ libpngMaincompat/d3d9gles(D3D9 on GLES 3.0, 155 shaders) — see RENDERER.mdInputplatform/input_android.cppimplementsInput.hfrom Android keys and touchFModSoundNFMSoundimplemented as a silent null back endNotable things the port had to fix
Three bugs in the original code only appear on a 64-bit target, and all three
crash immediately rather than degrade:
CBufferedStream::LoadBufferForcedshifted two pointers bynBufferStart - nPoscomputed in unsigned int. On Win32 the arithmetic wrapped around and gave
the intended negative offset; on arm64 it moves the pointer 4 GB away. This is
what made every
.respackage segfault.CBufferedStream::SetNewBufferSizetruncated a pointer difference toint.CRandomGenerator::FillRandRslseeded ISAAC by walkingC:\for a random fileand looping until it found one — an infinite loop anywhere else.
CStructureSaverused 4 bytes of an object's address as its on-diskreference ID. On 64-bit that truncates on write and half-fills a pointer on
read; the port keys references as
uint32and numbers objects densely onwrite. Format unchanged.
And one that is not a bug but a trap:
Float2Intwas x87fld/fistp, whichrounds. A naive
(int)replacement truncates and silently shifts geometry;the port uses
lrintf.Licence
The engine sources are covered by the repository's LICENSE.md —
non-commercial use only. The porting layer in
compat/,platform/,tools/and
scripts/is part of this repository and carries the same terms.