Files
RustRaptor/docs/upstream-patches.md
T
raptorreplica 214789fe87 Fork the vendored engine into the playable replica
Engine changes vs the vendored upstream (full ledger in
docs/upstream-patches.md):

- Assets: replace the encrypted FILE000N.GLB archives with a catalog of
  pre-decrypted loose files (data/INDEX); GLB_SlotItems() drives
  mission-pack / registered-version detection; payloads never run the
  GLB cipher at runtime.
- Platform: Linux/SDL2/ALSA only; Android, MSVC/MinGW, macOS packaging,
  bundled Windows SDL2 SDKs and the ENDOOM exit screen are gone;
  EXIT_Error now prints before exiting.
- Rendering: decouple presentation from simulation -- sim stays at the
  original 70/3 Hz, frames render interpolated at display rate (player,
  map scroll, enemies, shots, bonuses, anims, shadows); menus keep 70 Hz
  pacing; vsync with a delay-guard fallback.
- QoL: borderless window mode (Video/borderless), no menu cursor
  snapping onto default buttons.

Fix four regressions from the render split:

- Shield bars never redrew: RAP_ThinkStats latches g_oldshield each sim
  step before RAP_DisplayStats' dirty check could fire. Bars now draw
  unconditionally per rendered frame; g_oldsuper deleted, g_oldshield
  kept as sim-side damage-detection state only.
- Dying ships despawned without exploding: the render loop never called
  ANIMS_DisplayHigh(); most air-death explosions register HIGH_AIR.
  Restored between the player and enemy shots, as upstream orders it.
- Laser turret broke: reusing doneflag as a draw marker (it is
  hit/retire game state, and the turret retire path never frees its
  shot) hid the beam on hitting ticks and leaked one SHOTS pool slot
  per fire until no weapon could shoot. S_LINE tracers now carry an age
  counter: one Think pass, every render of their birth step, purged at
  the top of the next Think.
- Muzzle flash was cleared per rendered frame, making it invisible at
  display refresh rates; now cleared once per simulation step.
2026-08-26 18:45:07 +10:00

7.1 KiB

Patches against upstream

Base: nukeykt's reconstructed Raptor source (skynettx/raptor mirror), vendored in raptor/ (GPLv2+, see raptor/LICENSE and raptor/VENDOR.txt). Everything below diverges from upstream on purpose. Keep this list current when touching engine code.

Asset system: catalog of loose files (largest change)

Upstream reads seven encrypted FILE000N.GLB archives via src/glbapi.cpp. This fork replaces the archive reader with a catalog of loose files:

  • GLB_LoadCatalog() parses data/INDEX (<hex handle> <size> <path>, sorted); GLB_CatFind() binary-searches it; GLB_Load() reads the file.
  • GLB_SlotItems(slot) (new public API, src/glbapi.h) counts a slot's assets; src/rap.cpp uses it for mission-pack and registered-version detection instead of probing for .GLB filenames.
  • GLB_InitSystem keeps its signature but ignores the archive-name prefix parameter.
  • Payloads are pre-decrypted at extraction time; the runtime never runs the GLB cipher. GLB_EnCrypt/GLB_DeCrypt remain for reference/tooling.
  • FILEDESC shrank to {item, items}; the archive file-handle cache (GLB_FindFile/GLB_OpenFile/GLB_CloseFiles) was removed.

Rationale and format docs: glb-format.md, asset-pipeline.md.

Exit: no ENDOOM screen

Upstream's ShutDown() (src/rap.cpp) loaded LASTSCR*_TXT and opened a second, text-mode "farewell" window (I_LASTSCR, src/i_lastscr.cpp) that blocked until a keypress. Removed: ShutDown now closes the game window and exits directly; src/i_lastscr.cpp/.h deleted.

Diagnostics: EXIT_Error prints

Upstream's EXIT_Error (src/common.h) was a stub that called exit(0) without printing — every engine error died silently. It now prints its formatted message to stderr before exiting (still exit(0), matching the original exit code behavior).

Platform: Linux-only

  • CMakeLists.txt reduced to the Linux/SDL2/ALSA build; the Android (FetchContent SDL), Windows (MSVC/MinGW + rsrc/ resources) and macOS branches are gone.
  • Deleted upstream trees: raptor/android/, msvc/, gcc/, pkg/, rsrc/, win32build.bat, osxbuild.sh, the bundled Windows SDL2 SDKs under include/, and the SETUP(*).INI distribution templates.
  • src/loadsave.cpp: removed the __ANDROID__ block that copied .GLB files to external storage (obsolete concept in this fork).

Data detection

src/rap.cpp: mission-pack presence is derived from catalog slot counts (GLB_SlotItems) rather than access("FILE000N.GLB") probes; the fatal "data files NOT FOUND" check now keys off slot 0 / no packs.

Rendering: decoupled presentation loop

The original game advanced simulation and presentation in lockstep at 70/3 Hz (~23 fps): Do_Game() ran one think+draw pass, then waited three timer ticks. This fork keeps the simulation at exactly that rate but presents interpolated frames at display rate:

  • Simulation is paced by a wall-clock accumulator (RAP_SIM_STEP in src/rap.cpp, units of 1/70000 s = 3 ticks). Input handling, all *_Think(), DEMO_Think() (one attract-mode record per step), palette fades and the stats state machine run once per step, preserving game speed, demo playback timing and RNG consumption order.
  • After each step the scene is redrawn and presented with an interpolation fraction until the next step is due (GFX_SetInterp / GFX_InterpCoord, src/gfxapi.cpp). When behind schedule (blocking menus ate wall time) renders are skipped so the simulation catches up, matching the old tick-wait behavior.
  • Draw positions are lerped between the previous and current simulation states for player ship, tile map scroll, enemies, enemy/player shots, bonuses, anims and shadows. The map scroll interpolates an unwrapped counter (vscroll in TILE_Scroll) because raw tileyoff wraps by 32 px while the on-screen tile grid stays positionally continuous; TILE_Scroll runs before TILE_Think so wrap intervals render seamlessly.
  • State changes that used to ride inside draw calls were moved to simulation or gated per step: enemy engine-flame toggle (eframe, ENEMY_Think), beam-shot animation counter (SHOTS_Think), ion-scanner sweep (OBJS_DisplayStats, gated by gl_cnt), S_LINE tracers are purged by SHOTS_Think instead of being removed mid-draw. The S_LINE purge keys off a per-shot age counter (one simulation step of life, matching upstream's draw-then-remove); the turret's retire path never frees its shot, so the purge must not reuse doneflag, which is hit/retire game state — reusing it leaked one SHOTS pool slot per turret fire until SHOTS_Get() could service no weapon, and hid the beam on hitting ticks.
  • RAP_DisplayStats was split into RAP_ThinkStats (state: death sequence, wave-exit control, low-shield warnings) and the drawing half. The shield bars are redrawn unconditionally each rendered frame: the old g_oldsuper/g_oldshield dirty check can never fire when the latch runs in RAP_ThinkStats before the draw, which froze the bars. g_oldsuper is gone; g_oldshield survives as the sim-side previous-step shield used for damage detection. The muzzle-flash ship pic (g_flash) is likewise cleared once per simulation step, not per rendered frame, or it would be invisible at display refresh rates.
  • The above-player anim layer (ANIMS_DisplayHigh) is drawn between the player and enemy shots as upstream does; most air-death explosions register HIGH_AIR, so dropping that call made dying ships vanish without exploding.
  • Vsync is enabled when the display reports a refresh rate; I_VsyncActive() lets the game loop fall back to a delay guard when the renderer ignores the flag.
  • Menus keep their 70 Hz GFX_DisplayUpdate pacing; intro/story screens still pace at 4 ticks (~17.5 fps).

Video: borderless window mode

New Video/borderless SETUP.INI option (VIDEO_LoadPrefs). When set, SetVideoMode creates a decoration-free window at the desktop resolution anchored at (0,0) (SDL_WINDOW_BORDERLESS). Alt+Enter cycles between the borderless desktop window and a normal decorated window while the mode is active. Exclusive fullscreen_width/height modes ignore it; if both fullscreen=1 and borderless=1 are set, fullscreen wins. The stale commented-out -borderless command-line snippet was removed.

Input: no cursor snapping

Dialog/menu code no longer warps the mouse cursor onto the default button: SWD_SetWindowPtr/SWD_SetFieldPtr were deleted along with all callers (store.cpp, windows.cpp), as were the direct PTR_SetPos(field-center) calls when opening ask dialogs, the difficulty select, the hangar keyboard navigation and the ship computer. Keyboard selection still highlights fields without moving the pointer. The mission-start PTR_SetPos(playerx, playery) in Do_Game is kept — it seeds mouse ship control, not menu focus.

Deliberately unchanged

  • Game logic, mission data formats, audio synthesis — untouched.
  • GLB_* API names and handle layout (slot << 16 | item) — kept so src/fileids.h and all call sites stay identical to upstream.