Skip to content

fix(ngs2): correct UserFx ids and preserve Delay compatibility - #9337

Merged
oneandonlydean merged 3 commits into
boykopovar:mainfrom
Th3EvilGod:codex/ngs2-userfx-module-id
Oct 23, 2026
Merged

oneandonlydean merged 3 commits into
boykopovar:mainfrom
Th3EvilGod:codex/ngs2-userfx-module-id

Conversation

@Th3EvilGod

@Th3EvilGod Th3EvilGod commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

What

Separate UserFx (0x19, 0x40001900 | index) from Delay (0x1d, 0x40001dxx). Reuse the existing 40-byte UserFx callback parameter under the corrected IDs, reject non-zero UserFx module state with INVALID_STATE_SIZE, and reject non-null UserFx options and custom-sampler UserFx until their unsupported contracts are established.

Preserve the existing Delay audio pass-through as requested by the maintainer. A one-buffer custom submixer with eight ports, no module state and option 20, 0, 4, 1000.0f, 0 now passes creation, preserving the rack shape reported for Void Terrarium (PPSA03061) in #5055. The path handles the general supported option family, without a title ID or those specific values in production code: type 0/1, 1..8 taps, finite length 1..20000 and reserved 0. Unknown options, module state and custom-sampler Delay throw. Every 0x40001dxx parameter throws an explicit Delay-not-implemented error before any callback or parameter state is changed.

Evidence: fma-rc's PS5 firmware 12.70 module reading, parameter dispatch 0x25ec4, rack creation 0x13b68, identifies UserFx and Delay, the 40-byte UserFx parameter (8-byte header, handler, three user-data words), and the Delay option layout/ranges. The earlier reading identifies UserFx as having no option or module state. These are attributed findings, not disassembly or hardware measurements I performed.

Delay processing/defaults remain unverified; this deliberately preserves existing host behavior and does not claim a working delay effect. The existing UserFx callback context, flags, null-handler behavior and callback-error model also remain unverified on console. Both limits are recorded in separate debt entries. Multi-buffer racks and other custom DSP modules remain outside this change; no title is claimed unblocked.

Rebased on #9233 at 3e00103497 (including its #8609 prerequisite), with main 1a745ae38e merged. This PR's own change is the single commit b553950ef, affecting four files relative to the combined prerequisite base 154ca3754e; Gain code, its ramp tests and its debt entry are retained. The main-targeting diff includes the prerequisite until it lands.

Tested

Windows 11 Pro, supported WinLibs MinGW-w64 GCC 15.2.0 r7, Release, full-runtime configuration with BUILD_TESTING=ON.

  • Regression control: with the new literal-ID Delay test on the rebased pre-Delay implementation (068856ef5), ngs2_custom aborts at rack creation with custom module id 0x1d is not implemented.
  • Final runtime libs build and both NID-patched NGS2 libraries: pass.
  • Final ctest --test-dir build/windows-cnd -R '^ngs2_' -V: 13/13 pass, 3.63 seconds.
  • Delay regression creates the reported one-buffer / eight-port / no-state rack with literal 0x1d and the 20-byte option. It compares every stereo PCM sample over consecutive grains, silence before input and after stop, and unchanged output after rejected parameters. It covers 1, 2 and 24 Delay modules, both option types and range endpoints, all 256 0x40001dxx IDs, and confirms no legacy UserFx callback runs. Null/wrong options, invalid option fields, NaN/infinity, module state/offset and custom-sampler Delay are rejected.
  • Existing UserFx tests use literal 0x19 / 0x40001900 | index, verify PCM and callback state at positions 0, 1 and 23, and retain invalid-state, parameter-size/index and custom-sampler rejection checks. Gain regressions remain enabled.
  • tools/check_conventions.py --base origin/main: passes with no findings; whitespace check passes.
  • Live overlap scan completed. feat(libSceNgs2): process the custom Gain module #9233 and its fix(libSceNgs2): return the custom rack creation errors #8609 prerequisite are ancestors. The scan compares this rebased branch with the old published fix(ngs2): correct UserFx ids and preserve Delay compatibility #9337 head; that row is this update replacing itself. fix(ngs2): retain PCM input when reporting decoded bytes #5548 shares a declaration already affected by the prerequisite, and docs(debt): list the NGS2 custom modules of PPSA33655 #8851 edits the same debt. Stale feat(ngs2): add experimental I3DL2 reverb #1384 and feat(ngs2): decode complete HE-VAG waveform blocks #5797 cannot be fully compared with main; their source changes were inspected and concern reverb/HE-VAG rather than this correction. These integration warnings remain visible; no unrelated PR is modified.

No actual title, console-hardware or Linux run, no full repository test-suite run, and no performance claim. The synthetic Delay regression establishes preservation of existing PCM behavior, not console equivalence or game compatibility.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: feat(libSceNgs2): process the custom Gain module #9233
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

@KernelPanicNT

Copy link
Copy Markdown
Contributor

Thanks for picking this up so quickly, it's good to see 0x19 used as fma-rc read it. The move to 0x19 / 0x40001900 | index, INVALID_STATE_SIZE for a non-zero state and the throw for an option all line up with his two readings in #8039.

Two things worth a look before it goes in:

  1. Void Terrarium (PPSA03061) would stop. With this change 0x1d no longer matches any branch in Ngs2CheckCustomRack, so it hits the unknown-module throw (Custom.cpp:62-63), and the tests at tests/Ngs2Custom.cpp:470 and :584-588 expect that. Void Terrarium creates exactly that rack: one 0x1d module with the option 20, 0, 4, 1000.0f, 0, one buffer, no state (#5055 has a 150 s run reaching the title screen with sound). Today it gets through because main still accepts that option through the feat(ngs2): support custom submixer rack UserFx module 0x1d #5139 path.
    Throwing for a Delay we can't process yet is within the rules, so I'm not asking you to change the behaviour. It would just help to mention PPSA03061 in the PR body as a known regression and keep the title in the debt entry, so the maintainers can make that call knowingly. The other route is to keep main's 0x1d path under a Delay name until fma-rc tells us what Delay writes before any parameter is set (question 1 here). Either is fine by me.
  2. 0x19 on the custom sampler rack does nothing. Ngs2CheckCustomRack also runs for 0x4001 (Rack.cpp:106) and accepts 0x19 there, but 0x400019xx is only accepted on 0x4002 (Custom.cpp:202), so the handler can never be set and the audio passes through silently. To be fair, main has the same gap for 0x1d. fma-rc says the index is one higher on 0x4001, so it could either throw at creation there, as feat(libSceNgs2): process the custom Gain module #9233 does for Gain, or route the parameter with index - 1. Main's Filter handling on 0x4001 can stay as it is, since Dead Cells sends 0x40001300 and that's still an open question (question 5).

For what it's worth, the merge of #8609 in your branch is unchanged, so there's no conflict with mine, and the conflicts with #9233 are only textual, as your merge-order note already says.

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 8851 docs/dev/debt/libSceNgs2.native.md L24-25

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 1384 core/libs/prx/libSceNgs2.native/include/Ngs2Types.hpp
PR 5797 core/libs/prx/libSceNgs2.native/include/Ngs2Types.hpp
docs/dev/debt/libSceNgs2.native.md

@JJenkx

JJenkx commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Title data point: BLUE REFLECTION Quartet (PPSA33655). Its racks are listed in #8851:

  • MixerB: 0x19;
  • ReverbB: 0x15 Gain + 0x1b Reverb (12-byte option);
  • DuckingCompSB: 0x19 ×2, 2 buffers.

The stop moves forward with each PR. Same scripted runs, natively on Linux 7.2.9, Radeon RX 6650 XT, RADV Mesa 26.2.4, GCC Release:

Build Stop
main d6eafa54d0 2 s: NGS2: custom module id 0x19 is not implemented
d6eafa54d0 + #9337 ea6fc50b30 4 s: NGS2: the custom Gain module is not implemented
d6eafa54d0 + #9337 + #9233 8f6f1eac86 2 s: NGS2: the custom Reverb module is not implemented

So:

No audio was produced or checked; this only shows rack creation getting further. Combining the two PRs conflicted in Ngs2Types.hpp: I kept both SCE_NGS2_CUSTOM_VOICE_PARAM_GAIN and #9337's UserFx id 0x40001900. It also conflicted in the test and debt files, where I kept #9337's side; those are not built in this test.

@oneandonlydean oneandonlydean self-assigned this Oct 22, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 22, 2026
@oneandonlydean

Copy link
Copy Markdown
Collaborator

@Th3EvilGod thanks, the firmware error codes for the rack checks look good. One change needs more before we can take it:

UserFx 0x1d → 0x19. main accepts module 0x1d (and parameter 0x40001d00) as UserFx because of 40e2019, which fixed #5055: PPSA03061 creates a custom submixer rack with module 0x1d and aborted with NGS2: custom module id 0x1d is not implemented. With this PR, 0x1d isn't a known module any more, so that title hits the same throw again. A UserFx option, which main accepted, now throws as well.

Could you:

  1. say where 0x19 / 0x40001900 come from (firmware disassembly offsets, or an SDK header), and what 0x1d is in the firmware;
  2. keep Void Terrarium (PPSA03061) crashes on unresolved libc.prx import BKSCW2bCACA #5055's title working: if 0x1d is a different module, handle it (or at least that title's use of it), and don't let it fall through to the throw; and
  3. add a test that creates the Void Terrarium (PPSA03061) crashes on unresolved libc.prx import BKSCW2bCACA #5055 rack shape (custom submixer, module 0x1d) and asserts the result you expect.

The branch also conflicts with main after #7948 now, so a merge of main in the same push would help.

@Th3EvilGod

Copy link
Copy Markdown
Contributor Author

@oneandonlydean thanks for the review. The source is fma-rc's reading of the PS5 12.70 module, not a disassembly or console measurement I performed. It identifies UserFx as 0x19, its 40-byte parameter as an 8-byte header followed by the handler and three user-data words, and the parameter IDs as 0x40000000 | (module_id << 8) | index. That gives 0x40001900 | index on the custom submixer. The report cites parameter dispatch at 0x25ec4 and rack creation at 0x13b68. It identifies 0x1d as Delay, with the 20-byte type / maxTaps / maxLength / reserved option matching PPSA03061's rack.

Agreed that the current PR reintroduces the #5055 startup stop; documenting it does not preserve the working title. The remaining gap is Delay's default processing before any parameter is supplied. That is explicitly asked in the follow-up firmware questions, and I have not found an answer establishing that pass-through is the console behavior.

Would you accept retaining main's existing 0x1d creation/audio path temporarily under an explicit Delay compatibility name, separate from the corrected 0x19 UserFx path, with its unverified processing recorded in debt? The regression would create the complete reported one-buffer / eight-port / no-state rack with the 20-byte option and check creation plus the existing PCM behavior. That would establish preservation of current host behavior, not console-equivalent Delay support; unsupported Delay parameter processing would still throw. It would be a general path, not a title-ID check or a special case for those option values.

If retaining that behavior conflicts with the project's requirement to throw on unknown semantics, I will hold the mapping change until Delay's defaults are sourced rather than inventing a successful implementation. I am also inspecting the current-main conflicts so they can be resolved without dropping upstream changes.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@Th3EvilGod thanks, that answers where 0x19 comes from, and the parameter dispatch and rack creation offsets in fma-rc's report are enough for me on the renumbering.

On 0x1d: yes, please keep it, as you proposed. On main, a 0x1d module whose voices never get a 0x40001d00 parameter has no handler, so it passes audio through. That's what PPSA03061 runs on today, so keeping it preserves current behaviour rather than inventing new semantics. Concretely:

  1. Name it for what it is (Delay, 0x1d). Accept the reported rack at creation (1 buffer, 8 ports, no state, the 20-byte option) and pass audio through. Give it its own debt entry: Delay's processing is unverified, and its defaults are the open question in Rise of the Ronin (PPSA04540): NGS2 custom racks need several buffers, module ids 0x15/0x19/0x1b/0x23 and rack id 0x4003 #8039.
  2. Any Delay parameter (0x40001d00 | index) should throw "not implemented", so nothing silently pretends to delay.
  3. Add the regression you described: create that rack and check creation plus the existing PCM pass-through.
  4. Keep 0x19 / 0x40001900 as UserFx with the 40-byte parameter.

One more thing: #9233 carries the same rack-creation checks and is ahead of this in the merge queue. Please rebase onto #9233's branch (or onto main once it lands), so this PR's diff is only the UserFx renumbering and the Delay path. 7903c6d brings in current main but not #9233, so it will conflict with it.

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 22, 2026
@Th3EvilGod
Th3EvilGod force-pushed the codex/ngs2-userfx-module-id branch from 7903c6d to b553950 Compare October 22, 2026 21:22
@Th3EvilGod Th3EvilGod changed the title fix(ngs2): use the firmware UserFx module and parameter ids fix(ngs2): correct UserFx ids and preserve Delay compatibility Oct 22, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 23, 2026
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 23, 2026
@oneandonlydean
oneandonlydean merged commit 3d9c01d into boykopovar:main Oct 23, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants