Repository navigation
Conversation
|
✅Static analysis result - no issues found! ✅ |
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
The large native port and custom task lifecycle contain unresolved concurrency, memory-safety, and load-failure issues.
Review effort: Balanced
Findings: 3
Open (10)
Reject PSRAM task stacks when cache-disabled I/O is possible · New Delay task storage release until FreeRTOS cleanup completes · New Handle zero-duration volume transitions · New Reclaim task stacks and TCBs when slots are reused · New Handle GV_NewActor allocation failure · New Abort loading when the stage sector is missing · New Handle game heap allocation failure before dereferencing info · New Reject VAB ID 16 as out of bounds · New Enforce strict VAB ID upper bound · New Prevent duplicate MGS directory in extractor output · New
What changed in this PR
Adds Metal Gear Solid as a native ESP32-S3 core using the FoxdieTeam decompilation, psyz, and PSY-Q compatibility layers.
Changes:
- Adds MGS lifecycle, rendering, input, task, and virtual-CD integration.
- Vendors the game, psyz, and PSY-Q source trees.
- Adds ROM detection, cart registration, extraction tools, and setup documentation.
| File | Description |
|---|---|
CMakeLists.txt |
Enables the MGS component. |
README.md |
Documents support and setup. |
main/carts.hpp |
Registers the MGS cart. |
main/mgs_cart.hpp |
Implements cart lifecycle integration. |
components/rom_info/** |
Adds MGS identification and formatting. |
components/mgs/CMakeLists.txt |
Configures the native game build. |
components/mgs/linker.lf |
Places and brackets game statics. |
components/mgs/VENDOR.md |
Records vendored sources and modifications. |
components/mgs/include/mgs.hpp |
Defines the core interface. |
components/mgs/src/** |
Implements lifecycle, input, and presentation. |
components/mgs/tools/** |
Adds disc extraction and stage tools. |
components/mgs/shim/** |
Supplies compatibility headers. |
components/mgs/mgs/** |
Vendors the game and ESP32 port layer. |
components/mgs/psyz/** |
Vendors the PSX SDK reimplementation. |
components/mgs/psyq/** |
Vendors the PSY-Q library subset. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); | ||
|
|
||
| printf("LoadReq\n"); | ||
| work->info = FS_LoadStageRequest(dir); |
There was a problem hiding this comment.
Fixed in a8fcef1: NewLoader returns NULL when GV_NewActor fails.
There was a problem hiding this comment.
To confirm the earlier fix (a8fcef1): NewLoader returns NULL when GV_NewActor fails.
There was a problem hiding this comment.
ESP-IDF Size Report for 'Esp Box Emu'
FLASH uses app .bin size or json2 flash sum. RAM sums DRAM+IRAM via idf_size. Percentages shown when totals are available. |
Vendors the FoxdieTeam decompilation (davidmonterocrespo24's esp32-port branch), psyz and the PSY-Q decomp subset as components/mgs, with the game's three big buffers hosted in the 4MB ROM block, every libmgs static bracketed by linker symbols so the core can be fully stopped and relaunched without a reboot, and a gamepad/present layer for the box. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
soft_render.c binds its cached VRAM pointer from a constructor, which ran while g_RawVram was still NULL; the first ClearImage then faulted. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
… ChangeTh trace Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
An esp_timer watches the vblank tick and the frame counter and dumps the scheduler state and every task's backtrace through esp_rom_printf when they stall, so a wedged console still reports where it stopped. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
6c57566 to
c6275c7
Compare
cppcheck flagged the glue's section-bounds arithmetic (the linker symbols were declared as arrays, so subtracting them read as pointers to different objects) and a C-style cast; the vendored game, psyz and PSY-Q trees are excluded from the scan like the other cores' engines. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
pull_request_target runs the base branch's workflow, so the -i excludes only apply once merged; suppressions.txt is read from the PR tree. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
- esp32_threads.c: OpenTh fails instead of handing a task a PSRAM stack (which resets the chip on its first file read); a slot keeps its stack and TCB across CloseTh/OpenTh instead of leaking them per task exit; Mgs_ThreadsStopAll releases the storage only after FreeRTOS has retired the deleted tasks. - libsnd: a zero-duration crescendo no longer divides by zero; VAB ids are checked against NUM_VAB with a strict bound. - stageld.c / loader.c: a missing stage or a failed allocation aborts the load instead of reading a negative sector or dereferencing NULL. - README: the extractor writes MGS/ under its output directory, so it is pointed at the card root and the metadata entry uses MGS/STAGE.DIR. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
There was a problem hiding this comment.
🔵 Needs a closer look
The native port still has unresolved tooling, null-handling, RNG-compatibility, startup, and frame-buffer concurrency defects.
5 open findings
7 resolved since last review
Delay task storage release until FreeRTOS cleanup completes Reject PSRAM task stacks when cache-disabled I/O is possible Enforce strict VAB ID upper bound Reject VAB ID 16 as out of bounds Handle game heap allocation failure before dereferencing info Reclaim task stacks and TCBs when slots are reused Prevent duplicate MGS directory in extractor output
🧠 Review effort: Balanced
… (review) loader.c marked the actor as reading even when FS_LoadStageRequest() returned NULL, so its next Act handed NULL to FS_LoadStageSync(). PSY-Q's rand() is 15-bit and the game scales results by >> 15; the hosted libc's is 31-bit, so every file of the component now gets a narrowing wrapper through the force-included gtemac.h. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
…eview) The game clears GM_LoadComplete before asking for a loader and waits for the loader actor's Die to set it; when GV_NewActor fails that actor never exists, so NewLoader now sets it itself instead of leaving the game waiting forever. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1
| i = (size_t)snprintf(out, n, "%s/", mgs_data_root); | ||
| for (; *p && i + 1 < n; p++) { | ||
| if (*p == ';') break; | ||
| out[i++] = (*p == '\\') ? '/' : *p; | ||
| } | ||
| out[i] = 0; |
There was a problem hiding this comment.
Fixed in 150cd9d: when the data root alone does not fit, translate() terminates the buffer and returns instead of indexing with snprintf's wanted length.
…path buffer (review) snprintf returns the length it wanted on truncation; translate() used it as an index into the output buffer. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1



Summary
Adds Metal Gear Solid to esp-box-emu as a new core (
components/mgs). This is not an emulator: it is the FoxdieTeam decompilation of the PlayStation game compiled natively for the ESP32-S3, following davidmonterocrespo24's port — the PSX SDK is replaced by psyz (C reimplementation with a software GPU rasterizer and the GTE) plus the PSY-Q library decompilation, and the PSX kernel's threads/vblank run on FreeRTOS. Seecomponents/mgs/VENDOR.mdfor the vendored trees, upstream commits, licenses and every change made.What's in it
components/mgs/— the game (419 translation units + the port layer), psyz, PSY-Q subset, shim headers, and the box glue:src/mgs.cpplifecycle: the game runs in its own task on core 0;run_mgs_rom()only maps the gamepad and paces. VRAM (1MB), main RAM (~1.4MB), face group (512KB) and two RGB565 present buffers are carved from the 4MB ROM block via the pool allocator.libmgs.ais bracketed by linker SURROUND symbols (linker.lf: 128KB.bss→ PSRAM, 41KB.data→ DRAM);.datais snapshotted before the first launch and.bss/.datareset on deinit, after stopping the game at an mts-idle point and deleting its tasks. Verified on hardware: internal RAM returns to the byte across quit/relaunch cycles.mgs_input.cPSX digital-pad frames (D-pad, A=○, B=✕, X=□, Y=△, START, SELECT; SELECT+A/B/X/Y = L1/R1/L2/R2);mgs_present.cBGR555→RGB565 presenter.esp_rom_printf).main/mgs_cart.hpp,carts.hpp,rom_info(METAL_GEAR_SOLID, matched onSTAGE.DIR), top-level CMake (MGS_COMPONENTS/ENABLE_MGS), README systems table + setup section.Setup
Extract your own disc 1 image with
components/mgs/tools/extract_disc.pyintomgs/on the card and addmgs/STAGE.DIR, boxart/mgs.jpg, Metal Gear Solidtometadata.csv. Boots straight into the dock (s00a);MGS_START_STAGEpicks another stage.Status (BOX-3, 16MB PSRAM)
Test plan
🤖 Generated with Claude Code
https://claude.ai/code/session_01AWhoGwibqcKDhcG17mjVT1