Skip to content

feat(vcd): embed the BDMAssault variant pairs -- equip with zero user-supplied files (POPSLoader parity) - #251

Merged
NathanNeurotic merged 3 commits into
masterfrom
claude/bdma-embed-modules
Jul 21, 2026
Merged

feat(vcd): embed the BDMAssault variant pairs -- equip with zero user-supplied files (POPSLoader parity)#251
NathanNeurotic merged 3 commits into
masterfrom
claude/bdma-embed-modules

Conversation

@NathanNeurotic

@NathanNeurotic NathanNeurotic commented Jul 21, 2026

Copy link
Copy Markdown
Owner

POPSLoader has modules embedded in the elf, and then pastes them according to the VCD device if needed.

Done, byte-for-byte the same architecture:

  • 7 unique blobs vendored (modules/bdmassault/) with sha256-pinned provenance — the usbexfat/mx4sio usbd.irx is one shared blob (verified identical). All AFL-licensed (our license family); POPSLoader shipping these exact bytes is precedent. Full source table + caveats in PROVENANCE.md.
  • Gzipped at build (~194 KB raw → roughly half embedded; zlib already linked via libpng — zero new deps).
  • Seek order unchanged — Custom dir → boot device → game device still wins, so user-supplied files always beat embedded. The embedded pair replaces the old hard -4 "source files absent" failure — which was the guaranteed outcome of an MC boot. Sequential unpack (~48 KB transient), staged through the existing tmp→commit machinery.
  • The perf(vcd): stop re-equipping BDMA on every launch when the card already carries a driver pair #243 fast-path skip now logs (it silently ate the expected ATA equip on a card carrying an unmarked pair from earlier manual testing). Behavior unchanged by design: manual pairs stay trusted; one Settings equip writes the marker and hands control back to auto.

Paired with #250 (vendored atad): an out-of-the-box MC boot can now equip any variant with nothing pre-placed anywhere.

Builds clean.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added embedded BDMAssault driver variants covering USB, exFAT, MX4SIO, MMC/EMMC, and ATA storage configurations.
    • Automatically installs built-in fallback drivers when expected memory-card files are missing.
  • Bug Fixes
    • Improved BDMAssault equipment handling so it no longer fails immediately when source variant files can’t be found; updated the meaning of the -4 outcome when fallback installation also can’t proceed.
    • Added logging when an unmarked manual driver pair is already present.
  • Documentation
    • Added licensing and provenance details for the bundled driver components.

NathanNeurotic and others added 2 commits July 21, 2026 08:40
…O user-supplied files (maintainer directive, POPSLoader parity)

Maintainer, after the ATA BDMA equip silently failed on an MC boot: "POPSLoader has modules embedded
in the elf, and then pastes them according to the VCD device if needed." Implemented exactly that:

- modules/bdmassault/: the 7 UNIQUE variant blobs (usbexfat/mx4sio/mmce/ata pairs; usbd.irx.mx4sio
  is byte-identical to usbd.irx.usbexfat and shipped once -- sha256-verified), with the AFL v2.0
  LICENSE and a full PROVENANCE.md (pinned sources, sha256 table, the mutable-ATA-Assault-tag
  warning, the unpinned-mmce-lineage caveat). All sources are AFL -- same family as OPL; POPSLoader
  (GPL-3) shipping these identical bytes embedded is direct precedent.
- Build: each blob gzipped (-9 -n, reproducible) + BIN2C'd (~194 KB raw -> ~half embedded); zlib is
  already linked via libpng, so the inflate costs zero new dependencies.
- vcdEquipBdma: the seek order is UNCHANGED (Custom POPSTARTER dir -> boot device POPS/ -> game
  device family) so user-supplied newer files still WIN -- the embedded pair is the FINAL FALLBACK,
  filling what used to be a hard -4 "source files absent" failure (the guaranteed outcome of an MC
  boot, where no device carries a POPS folder). Unpack is sequential (one blob inflated at a time,
  ~48.5 KiB transient cap), staged through the existing tmp -> commit machinery, torn-pair handling
  identical.
- The #243 no-marker manual-pair fast path now LOGS its skip (it silently ate the maintainer's
  expected ATA equip on a card left over from manual testing). Behavior deliberately unchanged --
  a manual pair is still trusted, never clobbered; one Settings-screen equip writes the marker and
  restores auto-correction.

With this + the vendored-atad PR, an out-of-the-box MC boot can equip ANY variant with nothing
pre-placed on any device.

Builds clean (make opl.elf, exit 0).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces embedded BDMAssault variant driver pairs as a final fallback when no user-supplied drivers are found on any seek-path device. The drivers are gzipped at build time, embedded using bin2c, and inflated at runtime using zlib. Feedback on the changes suggests using the existing vcdSafeWriteFile function instead of introducing a redundant vcdWriteBufFile helper, along with adding defensive size checks. Additionally, it is recommended to propagate specific error codes (such as out-of-space or IO errors) during staging rather than unconditionally returning a generic error code.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/vcdsupport.c Outdated
Comment on lines +793 to +830
// Write a memory buffer to path (O_CREAT|O_TRUNC). Removes the partial file on any failure --
// same contract as vcdSafeCopyFile, for the private staging names only.
static int vcdWriteBufFile(const char *path, const unsigned char *buf, unsigned int len)
{
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd < 0)
return -3;
int wr = write(fd, buf, len);
int cr = close(fd);
if (wr != (int)len || cr < 0) {
unlink(path);
return -3;
}
return 0;
}

// Unpack the embedded pair for `mode` into the two staging paths, SEQUENTIALLY (one blob inflated at
// a time caps the transient heap at ~48.5 KiB + zlib state). Any failure leaves no partial staging
// file behind (vcdWriteBufFile removes its own partials; the caller unlinks both names regardless).
static int vcdStageEmbeddedPair(int mode, const char *tmp0, const char *tmp1)
{
if (mode <= VCD_BDMA_FAT32 || mode >= VCD_BDMA_MODE_COUNT || vcdBdmaEmbedded[mode].usbdGz == NULL)
return -1;
unsigned char *buf = NULL;
unsigned int len = 0;
if (vcdInflateGzip((const unsigned char *)vcdBdmaEmbedded[mode].usbdGz, (unsigned int)*vcdBdmaEmbedded[mode].usbdGzLen, &buf, &len) != 0)
return -1;
int r = vcdWriteBufFile(tmp0, buf, len);
free(buf);
if (r != 0)
return r;
buf = NULL;
if (vcdInflateGzip((const unsigned char *)vcdBdmaEmbedded[mode].hdfsdGz, (unsigned int)*vcdBdmaEmbedded[mode].hdfsdGzLen, &buf, &len) != 0)
return -1;
r = vcdWriteBufFile(tmp1, buf, len);
free(buf);
return r;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The helper function vcdWriteBufFile is redundant because vcdSafeWriteFile already exists and provides robust free-space checking (vcdMcHasSpace) and a proper write loop to handle partial writes. Additionally, checking the embedded sizes defensively before casting them to unsigned int prevents potential out-of-bounds reads if the sizes are somehow invalid or negative.

// Unpack the embedded pair for `mode` into the two staging paths, SEQUENTIALLY (one blob inflated at
// a time caps the transient heap at ~48.5 KiB + zlib state). Any failure leaves no partial staging
// file behind (vcdSafeWriteFile removes its own partials; the caller unlinks both names regardless).
static int vcdStageEmbeddedPair(int mode, const char *tmp0, const char *tmp1)
{
    if (mode <= VCD_BDMA_FAT32 || mode >= VCD_BDMA_MODE_COUNT || vcdBdmaEmbedded[mode].usbdGz == NULL)
        return -1;
    int usbdLen = *vcdBdmaEmbedded[mode].usbdGzLen;
    int hdfsdLen = *vcdBdmaEmbedded[mode].hdfsdGzLen;
    if (usbdLen < 18 || hdfsdLen < 18)
        return -1;
    unsigned char *buf = NULL;
    unsigned int len = 0;
    if (vcdInflateGzip((const unsigned char *)vcdBdmaEmbedded[mode].usbdGz, (unsigned int)usbdLen, &buf, &len) != 0)
        return -1;
    int r = vcdSafeWriteFile(tmp0, buf, len);
    free(buf);
    if (r != 0)
        return r;
    buf = NULL;
    if (vcdInflateGzip((const unsigned char *)vcdBdmaEmbedded[mode].hdfsdGz, (unsigned int)hdfsdLen, &buf, &len) != 0)
        return -1;
    r = vcdSafeWriteFile(tmp1, buf, len);
    free(buf);
    return r;
}

Comment thread src/vcdsupport.c
Comment on lines +1002 to +1010
if (useEmbedded) {
r = vcdStageEmbeddedPair(mode, tmp0, tmp1);
if (r != 0) {
unlink(tmp0);
unlink(tmp1);
if (diag != NULL && diagSize > 0)
snprintf(diag, diagSize, "No %s BDMA files on any device, and the built-in pair failed to unpack.", suffix);
return -4;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When vcdStageEmbeddedPair fails, returning -4 unconditionally misreports the error to the user. If the staging fails due to the memory card being full (-2) or an IO error (-3), returning the actual error code r allows vcdEnsureBdmaForLaunch to display the correct warning (e.g., _STR_BDMA_ERR_SPACE or _STR_BDMA_ERR_IO) instead of incorrectly claiming that the source files are absent.

    if (useEmbedded) {
        r = vcdStageEmbeddedPair(mode, tmp0, tmp1);
        if (r != 0) {
            unlink(tmp0);
            unlink(tmp1);
            if (diag != NULL && diagSize > 0)
                snprintf(diag, diagSize, "No %s BDMA files on any device, and the built-in pair failed to unpack.", suffix);
            return (r == -2 || r == -3) ? r : -4;
        }
    }

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5b7a6004-3d90-4ecf-8b4e-a7a6534a8375

📥 Commits

Reviewing files that changed from the base of the PR and between 38b5d9c and 9b92d5a.

📒 Files selected for processing (2)
  • include/vcdsupport.h
  • src/vcdsupport.c
📜 Recent review details
⏰ Context from checks skipped due to timeout. (20)
  • GitHub Check: build-debug (ingame_ppctty_debug, :v20250725-2)
  • GitHub Check: build-lang
  • GitHub Check: build-debug-ps2dev-latest (eesio_debug)
  • GitHub Check: build-debug-ps2dev-latest (ingame_ppctty_debug)
  • GitHub Check: build-debug-ps2dev-latest (ingame_debug)
  • GitHub Check: build-variants (EXTRA_FEATURES=0, PADEMU=1)
  • GitHub Check: build-debug-ps2dev-latest (DTL_T10000=1)
  • GitHub Check: build-debug-ps2dev-latest (iopcore_ppctty_debug)
  • GitHub Check: build-variants (EXTRA_FEATURES=1, PADEMU=0)
  • GitHub Check: build-debug-ps2dev-latest (iopcore_debug)
  • GitHub Check: build-debug (ingame_debug, :v20250725-2)
  • GitHub Check: build-debug (iopcore_ppctty_debug, :v20250725-2)
  • GitHub Check: build-variants (EXTRA_FEATURES=0, PADEMU=0)
  • GitHub Check: build-variants-ps2dev-latest (EXTRA_FEATURES=1, PADEMU=1)
  • GitHub Check: build-variants-ps2dev-latest (EXTRA_FEATURES=0, PADEMU=0)
  • GitHub Check: build-debug (DTL_T10000=1, :v20250725-2)
  • GitHub Check: build-variants-ps2dev-latest (EXTRA_FEATURES=0, PADEMU=1)
  • GitHub Check: build
  • GitHub Check: build-ps2dev-latest
  • GitHub Check: build-debug (ingame_ppctty_debug, :v20250725-2)
🔇 Additional comments (2)
include/vcdsupport.h (1)

111-113: LGTM!

src/vcdsupport.c (1)

794-815: LGTM!

Also applies to: 964-997


📝 Walkthrough

Walkthrough

BDMAssault driver variants and provenance records are added, Makefile rules embed deterministic gzip-compressed blobs, and vcdsupport.c unpacks selected embedded pairs when external drivers are unavailable.

Changes

BDMAssault embedded fallback

Layer / File(s) Summary
Vendor BDMAssault driver assets
modules/bdmassault/*
Adds USB, MX4SIO, MMCE, and ATA driver binaries together with licensing and provenance documentation.
Build-time gzip embedding
Makefile, include/bdma_embed.h
Compresses driver variants deterministically, converts them into embedded C sources, links the generated objects, and declares their symbols.
Runtime embedded driver fallback
src/vcdsupport.c, include/vcdsupport.h
Selects embedded driver pairs when source files are unavailable, inflates them into temporary staging files, updates the documented -4 failure semantics, and logs skipped manual pairs.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant vcdEquipBdma
  participant EmbeddedBDMA
  participant zlib
  participant MemoryCard
  vcdEquipBdma->>EmbeddedBDMA: Select variant gzip blobs
  vcdEquipBdma->>zlib: Inflate both driver blobs
  zlib-->>vcdEquipBdma: Return uncompressed driver data
  vcdEquipBdma->>MemoryCard: Write staged driver pair
  MemoryCard-->>vcdEquipBdma: Confirm staged files
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: embedding BDMAssault variant pairs as a fallback without user-supplied files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/bdma-embed-modules

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/vcdsupport.c`:
- Around line 1001-1019: Update the return-code documentation for vcdEquipBdma
in its header comment to describe -4 as the case where no device has the BDMA
pair and the embedded fallback also fails, rather than simply source variant
files being absent. Keep the existing meanings for the other return codes
unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6227a9f1-dfec-4430-a2ef-63189825aec6

📥 Commits

Reviewing files that changed from the base of the PR and between 8ac163f and 38b5d9c.

📒 Files selected for processing (12)
  • Makefile
  • include/bdma_embed.h
  • modules/bdmassault/LICENSE
  • modules/bdmassault/PROVENANCE.md
  • modules/bdmassault/usbd.irx.ata
  • modules/bdmassault/usbd.irx.mmce
  • modules/bdmassault/usbd.irx.usbexfat
  • modules/bdmassault/usbhdfsd.irx.ata
  • modules/bdmassault/usbhdfsd.irx.mmce
  • modules/bdmassault/usbhdfsd.irx.mx4sio
  • modules/bdmassault/usbhdfsd.irx.usbexfat
  • src/vcdsupport.c
📜 Review details
⏰ Context from checks skipped due to timeout. (10)
  • GitHub Check: build-variants (EXTRA_FEATURES=1, PADEMU=0)
  • GitHub Check: build-debug-ps2dev-latest (iopcore_debug)
  • GitHub Check: build-debug-ps2dev-latest (ingame_debug)
  • GitHub Check: build-variants-ps2dev-latest (EXTRA_FEATURES=0, PADEMU=0)
  • GitHub Check: build-debug (iopcore_debug, :v20250725-2)
  • GitHub Check: build-debug-ps2dev-latest (ingame_ppctty_debug)
  • GitHub Check: build-debug (eesio_debug, :v20250725-2)
  • GitHub Check: build-variants (EXTRA_FEATURES=0, PADEMU=1)
  • GitHub Check: build-debug (iopcore_ppctty_debug, :v20250725-2)
  • GitHub Check: build-debug (ingame_debug, :v20250725-2)
🧰 Additional context used
🪛 ast-grep (0.44.1)
src/vcdsupport.c

[warning] 796-796: This call makes a world-writable file which allows any user on a machine to write to the file. This may allow attackers to influence the behaviour of this process by writing to the file.
Context: 0666
Note: [CWE-732]: Incorrect Permission Assignment for Critical Resource

(world-writable-file-c)

🪛 LanguageTool
modules/bdmassault/PROVENANCE.md

[style] ~35-~35: Consider an alternative for the overused word “exactly”.
Context: ... WARNING: that tag is MUTABLE, which is exactly why the bytes are vendored here with sh...

(EXACTLY_PRECISELY)

🔇 Additional comments (17)
modules/bdmassault/LICENSE (1)

1-167: LGTM!

modules/bdmassault/PROVENANCE.md (1)

1-49: LGTM!

modules/bdmassault/usbd.irx.ata (1)

1-117: LGTM!

modules/bdmassault/usbd.irx.mmce (1)

1-48: LGTM!

modules/bdmassault/usbd.irx.usbexfat (1)

1-79: LGTM!

modules/bdmassault/usbhdfsd.irx.ata (1)

1-95: LGTM!

modules/bdmassault/usbhdfsd.irx.mmce (1)

1-48: LGTM!

modules/bdmassault/usbhdfsd.irx.mx4sio (1)

1-38: LGTM!

modules/bdmassault/usbhdfsd.irx.usbexfat (1)

1-102: LGTM!

Makefile (2)

129-129: LGTM!


843-867: 📐 Maintainability & Code Quality

No action needed: gzip is already installed in the supported CI Alpine environments and listed in the host-tool check, so this doesn’t add a new build-tool requirement.

			> Likely an incorrect or invalid review comment.
include/bdma_embed.h (1)

1-20: LGTM!

src/vcdsupport.c (5)

27-28: LGTM!


758-791: LGTM! Correct ISIZE-based sizing, sane cap, and correct inflateInit2(&z, 15 + 16) gzip-only windowBits usage (confirmed against the zlib manual: adding 16 decodes gzip-only, matching data produced by gzip -9 -n -c). No leaks on any failure path.


795-807: 🔒 Security & Privacy | 💤 Low value

Static analysis flags world-writable file creation (0666).

On the PS2 memory-card/IOP filesystem POSIX permission bits aren't enforced, so this is very unlikely to matter in practice; flagging only because static analysis surfaced it and per its own severity classification.

Source: Linters/SAST tools


809-830: LGTM! Sequential inflate correctly bounds transient heap use as documented, and cleanup on any staging failure is handled by the caller's unconditional unlink(tmp0)/unlink(tmp1).


1101-1109: LGTM! Pure logging addition on the existing no-marker manual-pair fast path; no behavior change.

Comment thread src/vcdsupport.c
- vcdWriteBufFile deleted: the EXISTING vcdSafeWriteFile (free-space check + partial-write cleanup)
  stages the embedded pair (Gemini -- real, the helper was redundant and weaker).
- Staging failures propagate their REAL class: -2 card-full / -3 IO keep their specific toasts;
  only no-pair/unpack-failure maps to -4 (Gemini -- real, a full card mis-toasted as 'source absent').
- The -4 contract comment in vcdsupport.h updated for the embedded-fallback era (CodeRabbit -- real).

#250's Gemini findings (NULL param + device bounds in the vendored atad) are DECLINED under the
vendoring doctrine: all target upstream-verbatim code, byte-faithful to the pin except the four
documented FORK hunks; no OPL caller passes invalid units, and drive-by hardening of vendored source
makes every re-vendor a conflict festival. Recorded in ORIGIN.txt's spirit -- upstream fixes belong
upstream.

Builds clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@NathanNeurotic
NathanNeurotic merged commit 7a91b2b into master Jul 21, 2026
63 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant