feat: add Star Wars: Dark Forces (The Force Engine port) - #120
Merged
Merged
Conversation
Add a `darkforces` component built on BSzili's Amiga branch of The Force Engine (TFE v1.09.410, commit d116b7a9), which already strips the desktop subsystems and provides low-spec code paths. The `__AMIGA__` guards are renamed to `TFE_ESPBOX`; provenance and the list of vendored changes are in components/darkforces/VENDOR.md. ESP32 platform layer (components/darkforces/src/platform): - esp_render: the classic fixed-point renderer draws into BoxEmu frame buffer 0 (8-bit 320x200) and the palette is converted to RGB565. - esp_audio: FreeRTOS task mixing the iMuse 8-bit sound effects with the OPL3 (Nuked) MIDI music at 11025 Hz into the I2S output. - esp_thread / esp_memory / esp_stubs: FreeRTOS threads, PSRAM-backed memory regions, and stubs for the console, front end, GPU renderers, SF2/system MIDI, captions and reticle. - Large static engine state is placed in PSRAM through a linker fragment (CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY). Integration: DarkForcesCart (mirrors DoomCart), `.gob` ROM detection (point metadata.csv at DARK.GOB), gamepad mapping with a SELECT modifier and a d-pad driven virtual mouse for the game's menus, and README docs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
Allocating the engine's memory regions and the ~320KB classic renderer state from the 4MB ROM block (unused while Dark Forces runs), drop the unused float-renderer state stub (300KB), and size-optimize everything except the renderer/synth hot paths. PSRAM heap after boot goes from 3.8MB (ROM block allocation failed) to 4.5MB. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
Fixes found while testing on hardware: - Endian shim: the Amiga branch's SDL_endian.h declared a big-endian CPU, so every value read from the game files was byte swapped (crash opening DARK.GOB). - SD card mount allowed only 5 open files, but the engine keeps its GOB and LFD archives open; raise to 16 and guard the escape-menu loader. - Palette: hand BoxEmu native RGB565 like the other cores (colors were wrong). - Double-buffer the display: copy each finished frame into alternating display buffers so the display task never samples a frame mid-draw (flicker/tearing). - Audio: keep the I2S stream buffer a couple of chunks ahead of the box's fixed 60Hz consumer and lower mix levels (pops/clipping). - Cap the game loop at 50 fps and always block for a tick so the low priority touch/gamepad polling and LVGL tasks get CPU (pause menu took seconds to open). - Gamepad text entry for the agent name (SELECT + d-pad). - Saves go straight to the emulator's slot files (so the slot shows as used and the screenshot appears), loads no longer tear down the MIDI player (mutex destroyed while the load path still locks it), and the save system now nulls its buffers on shutdown (dangling realloc on the next launch). - Skip freeing a GPU-only model field after the level region has been cleared (use-after-free at level end / load). - Serialize the memory regions and pool allocator with a mutex: the iMuse MIDI thread and the game thread both allocate through them. - Force the palette to be re-applied after a load (screen stayed black). - Hang detector (prints last stage + task list through esp_rom_printf) and optional task watchdog hooks for diagnosing freezes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
Heap tracing showed the retained memory was one-time, not per-launch growth: - ~800KB of static file read buffers in the level, texture, INF, sprite, model and VUE loaders that keep the capacity of the largest file loaded; add espbox_free_*_scratch() hooks and call them at shutdown. - 7 cached GOB/LFD archives never closed at exit (each open FILE holds a 16KB stdio buffer): call Archive::freeAllArchives() at shutdown. - The Landru sound effects archive (jedisfx.lfd) lives in a static object that is never destructed: close it in lsystem_destroy(). Retained memory after quit goes from ~980KB to ~13KB. Heap tracing hooks stay in the glue under CONFIG_HEAP_TRACING_STANDALONE. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
Dark Forces (measured with per-caller region attribution on a BOX-3): - Free the Landru cutscene/menu sounds (~0.4MB) and the agent menu images (~0.8MB) when a mission starts, on both the normal and the load-from-save paths; the agent menu reloads its images on demand. - Release the loaders' scratch read buffers (~0.8MB) as soon as a level has loaded instead of only at shutdown. - Region memory accounting (per-caller table printed on pause, pool/heap totals in the fps line) for further tuning. In-level free PSRAM goes from 6.3MB to 7.6MB. Doom: several tables are declared const but written at runtime (default_comp, the menu_t definitions, helpstrings, the intermission animation tables). IDF 6 write-protects rodata mapped into PSRAM, so these writes now fault (Cache error in M_LoadDefaults / M_Init). Make exactly the written objects non-const (~3KB). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
Dark Forces: map all of libdarkforces' .bss/COMMON to external RAM instead of listing a few objects. The engine has ~150 objects with static state, which kept ~46KB of .bss in internal DRAM permanently, whether or not the game is running, reducing the internal RAM every other core can use for hot data. Component internal DRAM goes from 52KB to 11KB (the rest is initialized .data). Doom: I_FinishUpdate pushed the render buffer itself to the display task and kept rendering the next frame into it, so the display could convert rows that were already being redrawn; sprites (drawn last) flickered. Doom can't swap render targets (the status bar and border are drawn incrementally), so copy each finished frame into one of two 8-bit present buffers carved out of frame_buffer1 and push the copy, the same fix used for Dark Forces. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
Like the other cores, the port's large static buffers now live in the emulator's shared memory system and only take up RAM while Dark Forces is running. Each converted static keeps its name but becomes a pointer; the owning file provides an espbox_shared_*(bool alloc) hook (see tfe/TFE_System/espboxShared.h) and darkforces_shared_memory.cpp allocates them at game init and releases them with shared_mem_clear() at exit. Converted (24 objects, ~115KB): file stream work buffers and writeString scratch, OPL3 chip state, MIDI player note table and command buffer, Landru music sequences, parser line buffer, weapon table, palettes, settings and save-system path buffers, the wall renderer's sprite column buffer, the platform audio mix buffers, and the region attribution table. Left static: iMuse tables linked into lists that are not all reset on shutdown, objects with constructors, initialized tables, the OPL3 pan table (built once behind a flag) and the log buffer (used after shutdown). Permanent PSRAM .bss of the component: 163KB -> 51KB (internal DRAM unchanged at 11KB of initialized data). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
… the ROM block Dark Forces reads its data from the SD card, so BoxEmu's 4MB ROM block is free while it runs. Everything the port allocates now lives there: - libdarkforces.a's malloc/calloc/realloc/strdup/new/delete are renamed after the build (alloc_redirect.syms) to an allocator backed by the block (src/platform/esp_alloc.cpp); free/realloc are wrapped firmware-wide so block pointers always return to it. The block is only used inside Dark Forces entry points and threads, allocations carry a per-session magic, and the exit report lists anything still live. - The engine's large statics, regions and renderer state use the block too (replacing the shared_memory buffers, which could not be allocated on 8MB). - Static STL caches (textures, sprites, models, paths, settings, ...) are released at shutdown so nothing dangles into the block on relaunch. - Level sounds load on first play and the escape menu frames only while it is open (startup peak 5.1MB -> 3MB). - The audio task always yields a tick (it starved core 1 when OPL3 synthesis fell behind); OPL3 tables are in internal RAM. - GOB directory reads are validated. pool_allocator: O(1) frees with lazy coalescing and a first-free hint, plus pool_block_size() and pool_walk(). Shared fixes needed on the 8MB box: - sdkconfig.defaults: read-only data stays in flash (CONFIG_SPIRAM_RODATA off), freeing ~1.5MB of PSRAM heap; LVGL uses the system heap instead of a fixed 50KB pool, which ran out in the pause menu and halted LVGL. - The GUI and the emulator menu share one LVGL instance but locked separate mutexes, and both update LVGL from battery events: they now share BoxEmu::lvgl_mutex() (fixes a double free / crash in lv_event_send). Verified on the 8MB ESP-BOX: repeated Dark Forces sessions with save, quit, relaunch and load, followed by Doom and Sonic. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
Landru allocates and frees with whichever region is current (persistent "Landru" or "Cutscene"), so blocks are sometimes freed through the other region. The port's region allocator unlinked the block from the region it was given, which corrupted both lists: 18 cutscene film and palette blocks were then never released and stayed in the 4MB ROM block after every session that played a cutscene or loaded a save. Each block now records its owning region; frees and reallocs always use it. The exit report also identifies region blocks and their allocating code. Verified on the BOX-3: start, load, quit leaves 0 blocks in the ROM block. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical and moderate findings remain across build gating, lifecycle synchronization, rendering, parsing, and LVGL safety.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds Star Wars: Dark Forces through a vendored The Force Engine port, with ESP32 platform integration and shared memory, rendering, audio, LVGL, and Doom updates.
Changes:
- Integrates Dark Forces cartridge handling, metadata, controls, save/load, and documentation.
- Adds TFE allocation, rendering, audio, threading, and platform support.
- Updates PSRAM/LVGL/SD configuration and Doom presentation handling.
File summaries
| File | Description |
|---|---|
sdkconfig.defaults |
Updates PSRAM, LVGL, and external-memory configuration. |
metadata.csv |
Registers Dark Forces data. |
main/carts.hpp |
Adds cartridge dispatch. |
components/rom_info/src/rom_info.cpp |
Implements ROM information handling. |
components/rom_info/include/rom_info.hpp |
Defines ROM information interfaces. |
components/pool_allocator/include/pool_allocator.h |
Defines pool allocator APIs. |
components/menu/include/menu.hpp |
Updates menu LVGL synchronization. |
components/gui/include/gui.hpp |
Updates GUI LVGL synchronization. |
components/doom/src/doom.cpp |
Updates Doom rendering and presentation buffers. |
components/doom/prboom/wi_stuff.c |
Updates Doom platform integration. |
components/doom/prboom/doomstat.h |
Updates Doom runtime declarations. |
components/doom/prboom/doomstat.c |
Updates Doom runtime state. |
components/darkforces/tfe/TFE_System/Threads/Win32/threadWin32.h |
Provides Win32 thread compatibility definitions. |
components/darkforces/tfe/TFE_System/Threads/Win32/signalWin32.h |
Provides Win32 signal compatibility definitions. |
components/darkforces/tfe/TFE_System/Threads/Win32/mutexWin32.h |
Provides Win32 mutex compatibility definitions. |
components/darkforces/tfe/TFE_System/Threads/thread.h |
Defines TFE thread interfaces. |
components/darkforces/tfe/TFE_System/Threads/signal.h |
Defines TFE signal interfaces. |
components/darkforces/tfe/TFE_System/Threads/mutex.h |
Defines TFE mutex interfaces. |
components/darkforces/tfe/TFE_System/Threads/Linux/threadLinux.h |
Provides Linux thread compatibility definitions. |
components/darkforces/tfe/TFE_System/Threads/Linux/mutexLinux.h |
Provides Linux mutex compatibility definitions. |
components/darkforces/tfe/TFE_System/tfeMessage.h |
Defines TFE messaging support. |
components/darkforces/tfe/TFE_System/parser.h |
Defines parser interfaces. |
components/darkforces/tfe/TFE_System/memoryPool.h |
Defines TFE memory pool support. |
components/darkforces/tfe/TFE_System/log.cpp |
Implements TFE logging. |
components/darkforces/tfe/TFE_System/frameLimiter.h |
Defines frame limiting support. |
components/darkforces/tfe/TFE_System/espboxShared.h |
Defines ESP-BOX shared platform helpers. |
components/darkforces/tfe/TFE_System/endian.h |
Defines endian utilities. |
components/darkforces/tfe/TFE_System/CrashHandler/crashHandler.h |
Defines crash handler interfaces. |
components/darkforces/tfe/TFE_RenderShared/quadDraw2d.h |
Defines 2D quad drawing support. |
components/darkforces/tfe/TFE_RenderShared/lineDraw2d.h |
Defines 2D line drawing support. |
components/darkforces/tfe/TFE_RenderBackend/vertexBuffer.h |
Defines vertex buffer support. |
components/darkforces/tfe/TFE_RenderBackend/textureGpu.h |
Defines GPU texture support. |
components/darkforces/tfe/TFE_RenderBackend/shaderBuffer.h |
Defines shader buffer support. |
components/darkforces/tfe/TFE_RenderBackend/indexBuffer.h |
Defines index buffer support. |
components/darkforces/tfe/TFE_RenderBackend/dynamicTexture.h |
Defines dynamic texture support. |
components/darkforces/tfe/TFE_PostProcess/postprocesseffect.h |
Defines post-processing effects. |
components/darkforces/tfe/TFE_PostProcess/postprocess.h |
Defines post-processing support. |
components/darkforces/tfe/TFE_PostProcess/overlay.h |
Defines post-processing overlays. |
components/darkforces/tfe/TFE_PostProcess/bloomThreshold.h |
Defines bloom threshold processing. |
components/darkforces/tfe/TFE_PostProcess/bloomMerge.h |
Defines bloom merge processing. |
components/darkforces/tfe/TFE_PostProcess/bloomDownsample.h |
Defines bloom downsampling. |
components/darkforces/tfe/TFE_PostProcess/blit.h |
Defines post-process blitting. |
components/darkforces/tfe/TFE_Polygon/polygon.h |
Defines polygon rendering support. |
components/darkforces/tfe/TFE_Outlaws/outlawsMain.h |
Defines Outlaws compatibility interfaces. |
components/darkforces/tfe/TFE_Outlaws/outlawsMain.cpp |
Implements Outlaws compatibility support. |
components/darkforces/tfe/TFE_Memory/memoryRegion.h |
Defines memory region management. |
components/darkforces/tfe/TFE_Memory/chunkedArray.h |
Defines chunked array storage. |
components/darkforces/tfe/TFE_Jedi/Task/taskMacros.h |
Defines task macros. |
components/darkforces/tfe/TFE_Jedi/Renderer/textureInfo.h |
Defines renderer texture information. |
components/darkforces/tfe/TFE_Jedi/Renderer/rwallSegment.h |
Defines wall segment rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/rwallRender.h |
Defines wall rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/rsectorRender.cpp |
Implements sector rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/rscanline.h |
Defines scanline rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/robjectRender.h |
Defines object rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/rlimits.h |
Defines renderer limits. |
components/darkforces/tfe/TFE_Jedi/Renderer/redgePair.h |
Defines edge pair handling. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_GPU/spriteDisplayList.h |
Defines GPU sprite display lists. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_GPU/rsectorGPU.h |
Defines GPU sector rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_GPU/renderDebug.h |
Defines GPU renderer debugging. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_GPU/rclassicGPU.h |
Defines classic GPU rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_GPU/objectPortalPlanes.h |
Defines GPU portal planes. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_GPU/modelGPU.h |
Defines GPU model rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_GPU/debug.h |
Defines renderer debug support. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Float/screenDraw.h |
Defines floating-point screen drawing. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Float/rsectorFloat.h |
Defines floating-point sector rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Float/robj3d_float/robj3dFloat.h |
Defines floating-point 3D object rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Float/robj3d_float/robj3dFloat_TransformAndLighting.h |
Defines floating-point transforms and lighting. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Float/robj3d_float/robj3dFloat_PolygonSetup.h |
Defines floating-point polygon setup. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Float/robj3d_float/robj3dFloat_PolygonDraw.h |
Defines floating-point polygon drawing. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Float/robj3d_float/robj3dFloat_Culling.h |
Defines floating-point culling. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Float/robj3d_float/robj3dFloat_Clipping.h |
Defines floating-point clipping. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Float/rlightingFloat.h |
Defines floating-point lighting. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Float/rflatFloat.h |
Defines floating-point flat rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Float/redgePairFloat.h |
Defines floating-point edge pairs. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Float/rclassicFloat.h |
Defines classic floating-point rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Fixed/rwallFixed.h |
Defines fixed-point wall rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Fixed/rsectorFixed.h |
Defines fixed-point sector rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Fixed/robj3d_fixed/robj3dFixed.h |
Defines fixed-point 3D object rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Fixed/robj3d_fixed/robj3dFixed_TransformAndLighting.h |
Defines fixed-point transforms and lighting. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Fixed/robj3d_fixed/robj3dFixed_PolygonSetup.h |
Defines fixed-point polygon setup. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Fixed/robj3d_fixed/robj3dFixed_PolygonDraw.h |
Defines fixed-point polygon drawing. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Fixed/robj3d_fixed/robj3dFixed_Culling.h |
Defines fixed-point culling. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Fixed/robj3d_fixed/robj3dFixed_Clipping.h |
Defines fixed-point clipping. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Fixed/rlightingFixed.h |
Defines fixed-point lighting. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Fixed/rflatFixed.h |
Defines fixed-point flat rendering. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Fixed/redgePairFixed.h |
Defines fixed-point edge pairs. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Fixed/redgePairFixed.cpp |
Implements fixed-point edge pairs. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Fixed/rclassicFixedSharedState.cpp |
Implements shared fixed renderer state. |
components/darkforces/tfe/TFE_Jedi/Renderer/RClassic_Fixed/rclassicFixed.h |
Defines classic fixed-point rendering. |
components/darkforces/tfe/TFE_Jedi/Memory/list.h |
Defines TFE list storage. |
components/darkforces/tfe/TFE_Jedi/Memory/allocator.h |
Defines TFE allocation interfaces. |
components/darkforces/tfe/TFE_Jedi/Math/cosTable.h |
Provides cosine table data. |
components/darkforces/tfe/TFE_Jedi/Level/roffscreenBuffer.h |
Defines level offscreen buffers. |
components/darkforces/tfe/TFE_Jedi/Level/robject.h |
Defines level objects. |
components/darkforces/tfe/TFE_Jedi/Level/rfont.h |
Defines level font support. |
components/darkforces/tfe/TFE_Jedi/Level/levelTextures.h |
Defines level textures. |
components/darkforces/tfe/TFE_Jedi/Level/level.h |
Defines level management. |
components/darkforces/tfe/TFE_Jedi/InfSystem/infState.h |
Defines information system state. |
components/darkforces/tfe/TFE_Jedi/InfSystem/infElevatorUpdateFunc.h |
Defines elevator update behavior. |
components/darkforces/tfe/TFE_Jedi/IMuse/midiData.h |
Defines MIDI data structures. |
components/darkforces/tfe/TFE_Jedi/IMuse/imTrigger.h |
Defines iMuse triggers. |
components/darkforces/tfe/TFE_Jedi/IMuse/imSoundFader.h |
Defines iMuse sound fading. |
components/darkforces/tfe/TFE_Jedi/IMuse/imList.h |
Defines iMuse lists. |
components/darkforces/tfe/TFE_Jedi/IMuse/imList.cpp |
Implements iMuse lists. |
components/darkforces/tfe/TFE_Jedi/IMuse/imDigitalSound.h |
Defines iMuse digital sound. |
components/darkforces/tfe/TFE_Jedi/IMuse/imConst.cpp |
Implements iMuse constants. |
components/darkforces/tfe/TFE_Game/saveSystem.h |
Defines save system support. |
components/darkforces/tfe/TFE_Game/reticle.h |
Defines reticle support. |
components/darkforces/tfe/TFE_Game/igame.h |
Defines game interfaces. |
components/darkforces/tfe/TFE_FrontEndUI/profilerView.h |
Defines profiler UI support. |
components/darkforces/tfe/TFE_FrontEndUI/modLoader.h |
Defines mod loading support. |
components/darkforces/tfe/TFE_FrontEndUI/frontEndUi.h |
Defines front-end UI support. |
components/darkforces/tfe/TFE_FrontEndUI/editorTexture.h |
Defines editor texture support. |
components/darkforces/tfe/TFE_FileSystem/filewriterAsync.h |
Defines asynchronous file writing. |
components/darkforces/tfe/TFE_FileSystem/fileutil.h |
Defines file utilities. |
components/darkforces/tfe/TFE_DarkForces/weaponFireFunc.h |
Defines weapon firing behavior. |
components/darkforces/tfe/TFE_DarkForces/vueLogic.h |
Defines VUE logic. |
components/darkforces/tfe/TFE_DarkForces/util.h |
Defines Dark Forces utilities. |
components/darkforces/tfe/TFE_DarkForces/util.cpp |
Implements Dark Forces utilities. |
components/darkforces/tfe/TFE_DarkForces/updateLogic.h |
Defines update logic. |
components/darkforces/tfe/TFE_DarkForces/random.h |
Defines random number support. |
components/darkforces/tfe/TFE_DarkForces/random.cpp |
Implements random number support. |
components/darkforces/tfe/TFE_DarkForces/playerLogic.h |
Defines player logic. |
components/darkforces/tfe/TFE_DarkForces/playerCollision.h |
Defines player collision handling. |
components/darkforces/tfe/TFE_DarkForces/pickup.h |
Defines pickup behavior. |
components/darkforces/tfe/TFE_DarkForces/mission.h |
Defines mission support. |
components/darkforces/tfe/TFE_DarkForces/Landru/textCrawl.h |
Defines text crawl support. |
components/darkforces/tfe/TFE_DarkForces/Landru/ltimer.h |
Defines Landru timing support. |
components/darkforces/tfe/TFE_DarkForces/Landru/lsystem.h |
Defines Landru system support. |
components/darkforces/tfe/TFE_DarkForces/Landru/lrect.h |
Defines Landru rectangles. |
components/darkforces/tfe/TFE_DarkForces/Landru/lmusic.h |
Defines Landru music support. |
components/darkforces/tfe/TFE_DarkForces/Landru/lfont.h |
Defines Landru font support. |
components/darkforces/tfe/TFE_DarkForces/Landru/ldraw.h |
Defines Landru drawing support. |
components/darkforces/tfe/TFE_DarkForces/Landru/lcanvas.h |
Defines Landru canvas support. |
components/darkforces/tfe/TFE_DarkForces/Landru/lactorDelt.h |
Defines Landru delta actors. |
components/darkforces/tfe/TFE_DarkForces/Landru/lactorCust.h |
Defines Landru custom actors. |
components/darkforces/tfe/TFE_DarkForces/Landru/lactorAnim.h |
Defines Landru animated actors. |
components/darkforces/tfe/TFE_DarkForces/Landru/cutsceneList.h |
Defines cutscene lists. |
components/darkforces/tfe/TFE_DarkForces/Landru/cutscene.h |
Defines cutscene support. |
components/darkforces/tfe/TFE_DarkForces/Landru/cutscene_player.h |
Defines cutscene playback. |
components/darkforces/tfe/TFE_DarkForces/hud.h |
Defines the game HUD. |
components/darkforces/tfe/TFE_DarkForces/generator.h |
Defines generator behavior. |
components/darkforces/tfe/TFE_DarkForces/GameUI/uiDraw.h |
Defines game UI drawing. |
components/darkforces/tfe/TFE_DarkForces/GameUI/pda.h |
Defines PDA UI support. |
components/darkforces/tfe/TFE_DarkForces/GameUI/missionBriefing.h |
Defines mission briefing UI. |
components/darkforces/tfe/TFE_DarkForces/GameUI/menu.h |
Defines game menu UI. |
components/darkforces/tfe/TFE_DarkForces/GameUI/escapeMenu.h |
Defines escape menu UI. |
components/darkforces/tfe/TFE_DarkForces/GameUI/editBox.h |
Defines edit box UI. |
components/darkforces/tfe/TFE_DarkForces/GameUI/delt.h |
Defines DELT UI assets. |
components/darkforces/tfe/TFE_DarkForces/GameUI/agentMenu.h |
Defines agent menu UI. |
components/darkforces/tfe/TFE_DarkForces/gameMusic.h |
Defines game music support. |
components/darkforces/tfe/TFE_DarkForces/gameMessage.h |
Defines game messages. |
components/darkforces/tfe/TFE_DarkForces/darkForcesMain.h |
Defines Dark Forces entry points. |
components/darkforces/tfe/TFE_DarkForces/config.h |
Defines game configuration. |
components/darkforces/tfe/TFE_DarkForces/config.cpp |
Implements game configuration. |
components/darkforces/tfe/TFE_DarkForces/cheats.h |
Defines cheat support. |
components/darkforces/tfe/TFE_DarkForces/briefingList.h |
Defines briefing lists. |
components/darkforces/tfe/TFE_DarkForces/automap.h |
Defines automap support. |
components/darkforces/tfe/TFE_DarkForces/animLogic.h |
Defines animation logic. |
components/darkforces/tfe/TFE_DarkForces/Actor/welder.h |
Defines welder actors. |
components/darkforces/tfe/TFE_DarkForces/Actor/turret.h |
Defines turret actors. |
components/darkforces/tfe/TFE_DarkForces/Actor/troopers.h |
Defines trooper actors. |
components/darkforces/tfe/TFE_DarkForces/Actor/sewer.h |
Defines sewer actors. |
components/darkforces/tfe/TFE_DarkForces/Actor/scenery.h |
Defines scenery actors. |
components/darkforces/tfe/TFE_DarkForces/Actor/phaseTwo.h |
Defines phase-two actors. |
components/darkforces/tfe/TFE_DarkForces/Actor/phaseThree.h |
Defines phase-three actors. |
components/darkforces/tfe/TFE_DarkForces/Actor/phaseOne.h |
Defines phase-one actors. |
components/darkforces/tfe/TFE_DarkForces/Actor/mousebot.h |
Defines mousebot actors. |
components/darkforces/tfe/TFE_DarkForces/Actor/flyers.h |
Defines flyer actors. |
components/darkforces/tfe/TFE_DarkForces/Actor/exploders.h |
Defines exploder actors. |
components/darkforces/tfe/TFE_DarkForces/Actor/enemies.h |
Defines enemy actors. |
components/darkforces/tfe/TFE_DarkForces/Actor/dragon.h |
Defines dragon actors. |
components/darkforces/tfe/TFE_DarkForces/Actor/bobaFett.h |
Defines Boba Fett actors. |
components/darkforces/tfe/TFE_DarkForces/Actor/animTables.h |
Defines actor animation tables. |
components/darkforces/tfe/TFE_Audio/systemMidiDevice.h |
Defines system MIDI device support. |
components/darkforces/tfe/TFE_Audio/MidiSynth/soundFontDevice.h |
Defines SoundFont device support. |
components/darkforces/tfe/TFE_Audio/midiPlayer.h |
Defines MIDI playback. |
components/darkforces/tfe/TFE_Audio/midiDevice.h |
Defines MIDI device interfaces. |
components/darkforces/tfe/TFE_Audio/audioOutput.h |
Defines audio output. |
components/darkforces/tfe/TFE_Audio/audioFilters.h |
Defines audio filters. |
components/darkforces/tfe/TFE_Audio/audioDevice.h |
Defines audio device interfaces. |
components/darkforces/tfe/TFE_Asset/vueAsset.h |
Defines VUE assets. |
components/darkforces/tfe/TFE_Asset/vocAsset.h |
Defines VOC assets. |
components/darkforces/tfe/TFE_Asset/textureAsset.h |
Defines texture assets. |
components/darkforces/tfe/TFE_Asset/spriteAsset.h |
Defines sprite assets. |
components/darkforces/tfe/TFE_Asset/paletteAsset.h |
Defines palette assets. |
components/darkforces/tfe/TFE_Asset/levelList.h |
Defines level lists. |
components/darkforces/tfe/TFE_Asset/imageAsset.h |
Defines image assets. |
components/darkforces/tfe/TFE_Asset/gmidAsset.h |
Defines GMID assets. |
components/darkforces/tfe/TFE_Asset/gifWriter.h |
Defines GIF writing support. |
components/darkforces/tfe/TFE_Asset/gameMessages.h |
Defines asset game messages. |
components/darkforces/tfe/TFE_Asset/fontAsset.h |
Defines font assets. |
components/darkforces/tfe/TFE_Asset/colormapAsset.h |
Defines colormap assets. |
components/darkforces/tfe/TFE_Asset/assetSystem.h |
Defines asset system interfaces. |
components/darkforces/tfe/TFE_Archive/zipArchive.h |
Defines ZIP archive support. |
components/darkforces/tfe/TFE_Archive/labArchive.h |
Defines LAB archive support. |
components/darkforces/tfe/TFE_Archive/gobMemoryArchive.h |
Defines GOB memory archives. |
components/darkforces/tfe/TFE_A11y/accessibility.h |
Defines accessibility support. |
components/darkforces/src/platform/esp_platform.h |
Defines ESP32 platform integration. |
components/darkforces/linker.lf |
Defines Dark Forces linker placement. |
components/darkforces/include/darkforces.hpp |
Exposes Dark Forces integration APIs. |
components/darkforces/alloc_redirect.syms |
Defines allocator symbol redirection. |
components/box-emu/src/box-emu.cpp |
Updates shared emulator behavior. |
components/box-emu/include/box-emu.hpp |
Updates shared emulator interfaces. |
CMakeLists.txt |
Integrates the Dark Forces component. |
Review details
Suppressed comments (1)
components/darkforces/src/platform/esp_render.cpp:95
- The display queue stores only this raw pointer and
push_frame()is nonblocking, so the display task may still be converting the previous buffer when the two-frame toggle returns here andmemcpyoverwrites it. A queue-full/drop does not establish ownership either, so this can still tear or corrupt frames under display lag. Add buffer-release/acknowledgement or otherwise prevent reuse until consumption finishes.
- Files reviewed: 82/449 changed files
- Comments generated: 13
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| set(DOOM_COMPONENTS "doom") | ||
|
|
||
| ### DARK FORCES ### | ||
| set(DARKFORCES_COMPONENTS "darkforces") |
| { | ||
| TFE_Memory::DfAllocScope allocScope; | ||
| deinit_darkforces(); | ||
| init_darkforces(s_gobPath, nullptr, 0); |
Comment on lines
+76
to
+79
| s_curFrameBuffer = nullptr; | ||
| BoxEmu::get().palette(nullptr); | ||
| freeDisplayBuffers(); | ||
| } |
| static Thread* s_thread = nullptr; | ||
|
|
||
| #ifdef TFE_ESPBOX | ||
| static bool s_runMusicThread; |
Comment on lines
+136
to
+140
| } | ||
| } | ||
| } | ||
| } | ||
| s_line[linePos] = 0; |
Comment on lines
+98
to
+103
| if (s_audioThreadCallback) | ||
| { | ||
| lock(); | ||
| s_audioThreadCallback((f32*)s_sfxBuffer, AUDIO_CALLBACK_BUFFER_SIZE, 1.0f); | ||
| unlock(); | ||
| haveSfx = true; |
Comment on lines
+57
to
+58
| thread->m_isRunning = false; | ||
| xSemaphoreGive(thread->m_done); |
| { | ||
| m_blockComment = false; | ||
| } | ||
| else if (m_enableBlockComments && m_buffer[i] == '/' && m_buffer[i+1] == '*') |
Comment on lines
+218
to
+222
| uint8_t* dst = presentBuffer[presentIndex]; | ||
| if (dst) { | ||
| memcpy(dst, framebuffer, SCREENWIDTH * SCREENHEIGHT); | ||
| box.push_frame(dst); | ||
| presentIndex ^= 1; |
Comment on lines
+24
to
+25
| // Enable : as a seperator but do not remove it. | ||
| void enableColonSeperator(); |
8 tasks
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.
Description
Adds Star Wars: Dark Forces as a new system, built on The Force Engine (TFE), using BSzili's Amiga port as the base (already stripped of the desktop-only subsystems). The game plays from the original DOS data on the SD card. The PR also fixes shared issues found along the way (Doom crashes and sprite flicker, an LVGL race, and the 8MB ESP-BOX PSRAM budget).
Dark Forces
components/darkforces: vendored TFE subset (tfe/, changes listed inVENDOR.md) plus ESP32 platform code insrc/platform(render backend, audio, threads, memory regions, allocator).main/darkforces_cart.hpp: cart with gamepad mapping, emulator pause menu, and save/load to the emulator's save slots.metadata.csvreferencesdarkforces/DARK.GOB.ENABLE_DARKFORCESin the top-levelCMakeLists.txt.Memory
Everything Dark Forces allocates lives in BoxEmu's 4MB ROM block while it runs, which is unused because the game reads its data from the SD card.
malloc/free/new/deletecalls are renamed todf_*functions (alloc_redirect.syms). Those functions allocate from the pool (src/platform/esp_alloc.cpp).free/reallocare wrapped firmware-wide so pointers into the block always go back to the pool.pool_allocator: frees are now O(1) with lazy coalescing and a first-free hint. It also gainspool_block_size()andpool_walk().Shared changes (affect all cores)
sdkconfig.defaults:CONFIG_SPIRAM_RODATAoff). Copying it to PSRAM left the 8MB ESP-BOX with about 60 bytes of PSRAM at the menu; it now has about 1.48MB free.CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORYis enabled.GuiandMenushare one LVGL instance but locked separate mutexes, and both update LVGL from battery events. They now shareBoxEmu::lvgl_mutex(), which fixes a double free and crash inlv_event_send.const. On IDF 6, rodata is write-protected, so those writes faulted.max_filesgoes from 5 to 16, because the game keeps its archives open.Motivation and Context
Adds a new game to the emulator. It also makes the 8MB ESP-BOX usable with the larger firmware, and fixes crashes that affect other cores.
How has this been tested?
Tested on hardware:
Notes for review
ENABLE_DARKFORCESoff in published release builds, or deciding how you want to handle it.Types of changes
Checklist:
🤖 Generated with Claude Code
https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1