Skip to content

feat(libSceAjm): run PS4 batch buffer jobs in the console's chunk layout - #9637

Open
ntamas94 wants to merge 12 commits into
boykopovar:mainfrom
ntamas94:feat/libSceAjm-ps4-batch-buffer
Open

ntamas94 wants to merge 12 commits into
boykopovar:mainfrom
ntamas94:feat/libSceAjm-ps4-batch-buffer

Conversation

@ntamas94

@ntamas94 ntamas94 commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

What

Knowledge is Power: Decades (CUSA12435, Wwise), the one PS4 title observed, builds its AJM batches with the PS4 SDK's inline batch helpers, which end in four libSceAjm exports: sceAjmBatchJobControlBufferRa, sceAjmBatchJobRunBufferRa, sceAjmBatchJobRunSplitBufferRa and sceAjmBatchStartBuffer. Unlike the PS5 builders, whose batch records are private to the library, here the batch buffer belongs to the title: the SDK code reads the position each builder returns, sizes the buffer for the console's jobs and may write chunks itself. So the buffer has to use the console's layout. On main, #9433 added the PS4 libSceAjm with three of the four as throwing stubs, and sceAjmBatchJobRunBufferRa (ElslOCpOIns) is only a throwing stub in libSceAjmi.

This implements the layout in libSceAjm.native/src/Ajm.cpp, which libSceAjm is built from:

  • The builders write one job each: a job chunk (instance as payload, byte count of the job's chunks), an optional return-address chunk, the input buffers, a flags chunk (control or run, flags bits 32-51 as payload), the output buffers and the output control (sideband) buffer. They return the position after the job.
  • sceAjmBatchStartBuffer walks the chunks, skips inline-data chunks, turns each job into the library's existing control, run or statistics job and runs it with the existing decoders (ATRAC9, MP3, M4AAC, Opus), so the sideband results are the ones the PS5 path writes. Like sceAjmBatchStart, it rejects a context that initialization did not allocate, and the batch belongs to its context.
  • A chunk header is read as the console's builders write it: bits 0-3 the kind, bit 4 the output direction of a buffer chunk (0x11 output run, 0x12 output control), bits 6-25 the payload. The builders leave bits 5, 26, 27 and 31 as they were, and a title that reuses its batch memory carries the previous batch's bytes there, so those bits are ignored.
  • An ATRAC9 run with RUN_MULTIPLE_FRAMES reports which side ran out, as the console does: PARTIAL_INPUT (0x8) when the input ran out with room left, on a superframe boundary or not; NOT_ENOUGH_ROOM (0x10) when the room ran out with input left, after any number of frames, and with nothing consumed for a zero room; both (0x18) when the input ended just as the output filled, which is every Wwise run job. The ATRAC9 format reports the frame's bitrate (frame bytes x 8 x sample rate / 256: 168000, 84000 and 384000 measured). Resampled runs keep the earlier rule, as they were not measured.
  • A run job read from a PS4 batch buffer without RUN_MULTIPLE_FRAMES decodes exactly one ATRAC9 frame and reports 0, as the probe measured on the PS4. A job from the PS5 builders still decodes its whole input, which FMOD in PPSA29141 relies on; the PS5 console's behaviour there is not measured.
  • Buffer chunks follow what probe batches on the PS4 showed: a chunk of size zero is dropped whatever its address, so the job runs as without it, and a null address with a nonzero size refuses the whole batch before any of its jobs runs: sceAjmBatchStartBuffer returns 0x80930011 (MALFORMED_BATCH) and fills the error record with 0x80930016 (INVALID_ADDRESS), the job chunk's address, the chunk's offset in the job and the job's return address. A size that is not a multiple of 8, a chunk that overruns its job or the batch, and a job without a flags chunk are refused the same way, not measured; the record then holds 0x80930011. Unknown chunk kinds and control flags other than RESET, INITIALIZE and SIDEBAND_GAPLESS_DECODE throw.
  • The three stubs in libSceAjm/Export.cpp are dropped. The throwing sceAjmBatchJobRunBufferRa of libSceAjmi stays, declared as sceAjmBatchJobRunBufferRa_nid_disambig1: the NID patcher exports it under the name's NID, so a title importing it from libSceAjmi still gets the not-implemented error at call time, and the two libraries no longer export one name.

The layout (chunk kinds 0, 1, 2, 3, 4, 6, 7, 17, 18, the builders' chunk order, statistics instance 0x80000) follows shadPS4's PS4 library reimplementation. A GoldHEN user-mode plugin dumped 32 545 batches that Knowledge is Power: Decades (CUSA12435, Wwise) started on a PS4. They confirmed the 64-byte control and 80-byte run jobs byte for byte, the header bits above, and the 0x18 result and bitrate of all 14 680 traced run jobs, which each decode 4 frames into exactly 4 frames of room. Probe runs of one of the title's ATRAC9 streams through the same builders on the PS4 (fresh instance per case: input ending with room left, room ending with input left, half a superframe of room, zero room, exact fit) gave the result rules above, showed that a run without RUN_MULTIPLE_FRAMES decodes exactly one frame on the console (kept as the whole input here, which a PS5 title needs; noted in the debt), that the console rejects the tests' synthetic silent superframe after three frames where LibAtrac9 decodes four, and that LibAtrac9 matches the console's PCM of that stream within 4 LSB (83.5% of samples exactly). The statistics instance and the inline chunk come from shadPS4 only; the debt entries say what is measured and what is not. The PS5 sceAjmBatchStart path and its records are unchanged, apart from the ATRAC9 exact-fit result and bitrate, which both paths share.

Tested

Windows 11 Pro 26200, WinLibs GCC 15.2.0 r7, NVIDIA RTX 2080, on main at 7e69be6793, merged into this branch. This was first opened as Get-fix#17 against #6977's branch, where it was reviewed and extended with the PS4 measurements; it is opened here as one commit at Get-fix's request.

  • guest_ajm passes, with seven new cases:
    • TestConsoleBufferJobs builds a control job (with a return address), a run job and a split run job with the three builders, checks the returned positions and the chunk words, starts the batch with sceAjmBatchStartBuffer and checks the control result and both run sidebands (result, consumed, written, total samples, frames) and the decoded PCM.
    • TestConsoleHandWrittenJobs writes a batch word by word: an inline chunk holding ATRAC9 initialize parameters, a control job whose input points into it, a run job and a statistics job. It checks their results, then that a size not a multiple of 8, a job without flags and an overrunning job return 0x80930011, and that an unknown chunk kind throws.
    • TestConsoleStaleHeaderBits runs a hand-written batch with every bit the console's builders never write set, and checks it runs exactly as the clean batch.
    • TestAt9FlagRules runs the five fits above and checks the result, the bytes consumed, the output written and the frame count of each.
    • TestConsoleBufferEdges runs a job with zero-size input and output chunks beside real ones (decodes as without them), a job whose only output has size zero (0x10, nothing consumed), and two batches with a null input or output address, which are refused with the measured error record while the control job before them stays unrun.
    • TestConsoleEmptyBatch checks the empty-batch contract: a null batch address is 0x80930005 with size 0 or 16, a null batch id is 0x80930005, and a batch of zero bytes at a non-null address starts, returns an id and completes at once.
    • TestConsoleSingleFrameRun runs a console job without RUN_MULTIPLE_FRAMES on a whole superframe with room for four frames and checks that one frame is decoded with result 0, the PCM after it untouched.
    • The existing ATRAC9 cases whose input exactly fills the output now expect 0x18; the helpers that stream a decode accept 0x8 and 0x10 where one side runs out.
  • guest_ajm_vibration passes; its exact-fit run now expects 0x18 (it expected 0 before). Vibration streams were not in the trace.
  • check_conventions passes; the patched libSceAjmi.prx still exports ElslOCpOIns (objdump).
  • PS4 probe batches (FW 10.71, ps4probe, the console's builders, a control job before each run job): zero-size input and output chunks alone, beside a real chunk and with a null address; null addresses with a nonzero size as the first or second input chunk and as the output chunk. They gave the rules above; the refused batches ran nothing.
  • Knowledge is Power: Decades, locally on my bring-up branch, with the first version of this change (before the header-bit and result rules above): Wwise builds every batch with these builders and a return address (80-byte run jobs, 64-byte control jobs, up to 6 jobs per batch). In four 150 s runs it ran about 30,800 jobs per run on five stereo ATRAC9 instances (control flags 0x6001, run flags 0xc00000001001, 56-byte sidebands), every one with result 0 under that commit's rules, about 650 s of decoded PCM per run, and the title reached its animated title screen. One of the four runs stopped before that on a UnityGfxDeviceWorker fault in the guest heap that runs with every AJM job skipped also hit; it is not in AJM. Without this PR the title aborts at its first sceAjmBatchJobControlBufferRa (not implemented).

#9483 changes the last lines of sceAjmBatchStart's job loop, next to where this moves the batch registration into RegisterBatch, so the two conflict in three lines; #9481 and #9034 also add debt entries to libSceAjm.native.md. I can rebase onto whichever lands first.

Checklist

  • Based on current main (7e69be6793, merged in); no other open PR implements the same functions (searched boykopovar/AnyPS5 and Get-fix/AnyPS5 for Ajm, BufferRa and StartBuffer)
  • 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: fix(ajm): bound private batch job reads before execution #9483 (landing order agreed in its thread: it lands first, this follows); feat(libSceAjm): parse CRC-protected and non-stereo MP3 metadata frames #8468 is merged and merged in
  • Fixes: none
  • Unblocks: Knowledge is Power: Decades (CUSA12435) Knowledge is Power: Decades (CUSA12435) #9718, which stops at its first sceAjmBatchJobControlBufferRa without this
  • GPUs tested: n/a (no AGC change)
  • AI-assisted: yes (Claude Code)

PS4 titles build AJM batches with the SDK's inline helpers, which call
sceAjmBatchJobControlBufferRa, sceAjmBatchJobRunBufferRa and
sceAjmBatchJobRunSplitBufferRa and start the buffer with
sceAjmBatchStartBuffer. The buffer belongs to the title, so it has to use
the console's chunk layout rather than this library's private records.

The builders write console jobs (job, return address, buffer and flags
chunks) and return the position after them. StartBuffer checks the
context, walks the chunks, skips inline data and runs each job as the
library's existing control, run or statistics job. Chunk headers are read
as the console's builders write them: bits 0-3 the kind, bit 4 a buffer's
output direction, bits 6-25 the payload; the other bits keep a reused
buffer's old bytes on a PS4. Malformed batches return MALFORMED_BATCH,
unknown chunk kinds and unimplemented control flags throw.

An ATRAC9 run with RUN_MULTIPLE_FRAMES reports which side ran out as a
PS4 does (0x8 input, 0x10 room, 0x18 both), and the ATRAC9 format reports
the frame's bitrate; both were measured on a PS4 with Knowledge is Power:
Decades (Wwise) batch dumps and probe runs. The vibration test's exact-fit
run expects 0x18 accordingly.

The throwing stubs in libSceAjm and the libSceAjmi copy of
sceAjmBatchJobRunBufferRa are dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions github-actions Bot added the unblocks title Names a title ID on the Unblocks line label Oct 22, 2026
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Progress report (5e6ae6e → d924eb2)

📈 System libraries: 48.41% (+0.01%, +2 implemented, +1 declared, -1 removed functions)

✅ 2 implemented
Library Name
libSceAjm.native sceAjmBatchJobControlBufferRa
libSceAjm.native sceAjmBatchJobRunBufferRa
🆕 1 declared as stubs
Library Name
libSceAjmi sceAjmBatchJobRunBufferRa_nid_disambig1
🗑️ 1 removed
Library Name
libSceAjmi sceAjmBatchJobRunBufferRa
🔗 1 now sharing sources
Library Name
libSceAjm libSceAjm.native

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

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 3205 core/libs/tests/GuestAjm.cpp
PR 10179 docs/dev/debt/libSceAjm.native.md
PR 10221 docs/dev/debt/libSceAjm.native.md
PR 10292 core/libs/tests/GuestAjm.cpp
PR 10687 docs/dev/debt/libSceAjm.native.md

@fogydes

fogydes commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Two findings from review, the first is the one I would block on.

1. The removed libSceAjmi stub turns a runtime error into an unresolved import. The diff deletes sceAjmBatchJobRunBufferRa from core/libs/prx/libSceAjmi/Export.cpp. That export is in the PS5 3.20 export table for module libSceAjmi (the debt entry cites that list), so a title importing it under that module previously hit NotImplemented_nid_no_patch at call time; after this PR the import doesn't resolve and the title dies at startup instead — and per CONTRIBUTING missing imports must surface as clear startup errors, not as a silent behavioral change via another module. The PR keeps the sibling sceAjmBatchJobInlineBuffer stub in place, which is the declare-in-place pattern it follows elsewhere, and the name-disambiguation machinery (_nid_no_patch handling in tools/nid_names.py) exists precisely for this libSceAjm/libSceAjmi collision. Also, docs/dev/debt/libSceAjmi.md is unchanged on this branch and still says "5 named callable exports ... throw through NotImplemented_nid_no_patch", while the Export.cpp it links now contains only sceAjmBatchJobInlineBuffer. Either keep the stub, or remove the export intentionally and update that debt entry to say so.

Verified on the branch: the file now holds only the InlineBuffer stub, and the debt file is byte-identical to main's.

2. Asymmetric input/output buffering in RunConsoleJob's PS4-buffer block. Inputs are pushed when size != 0 alone, outputs require ptr && size. A null-input-with-nonzero-size batch then reaches the copy path; and a zero-size output room silently becomes a buffer-less run, where the PS5 path answers NOT_ENOUGH_ROOM under the new flag rules. If the PS4-measured behavior really differs in both cases, say so in the debt entry; otherwise the two paths should share the same predicate.

Also worth folding into the debt entry: the ATRAC9 result-flag values that the new // comments in GuestAjm.cpp carry ("PARTIAL_INPUT | NOT_ENOUGH_ROOM as on the console", the 0x8/0x10/0x18 splits) are console-measured behavior, and debt entries are where behavior provenance lives. The conventions gate passed only because the PR touches technical debt, so those comments are tolerated — the values should not live only in the test.

The chunk-walk bounds checks, the ATRAC9 bitrate math and the result-identity with the PS5 path all check out; no null-deref path reaches RegisterBatch from either call site.

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The PS4 trace evidence and title stop are substantial, and the batch bounds/result handling have received useful independent review. Current-head blockers remain:

  1. The deletion of sceAjmBatchJobRunBufferRa from libSceAjmi removes an existing throwing export. That changes a title importing this module from a call-time NotImplemented_nid_no_patch error to an unresolved import at startup, and docs/dev/debt/libSceAjmi.md still claims five exports throw although only one remains. Preserve the stub (the NID-name disambiguation supports the collision) or intentionally remove it with aligned debt documentation and evidence.
  2. In RunConsoleJob, inputs are queued for any nonzero size even with a null pointer, while outputs require both pointer and size. That can feed a null input into the copy path and silently omit a zero-size output buffer. Please align the predicates or add PS4-measured evidence and debt text for these cases.
  3. The tested title evidence is for PS4 CUSA12435, while the checklist says broad PS4 titles and does not link a Game bring-up discussion. Please identify the title ID/discussion that this is unblocking and scope claims to observed behavior.

Hosted eligibility/conventions/intake/labels/report pass; product test jobs were skipped by path filters. The detailed Windows tests and PS4 probes are author-reported; I did not rerun the audio suite locally. The shared-code overlaps with #9483 and debt overlap with #8468 require an agreed order/dependency before merge.

ntamas94 and others added 2 commits October 22, 2026 11:38
…s as a PS4 does

Review of the PS4 batch buffer change. The libSceAjmi stub of
sceAjmBatchJobRunBufferRa stays, declared with the _nid_disambig1 suffix
so that both libraries export the NID without defining one name.

sceAjmBatchStartBuffer reads the whole batch before running any job, as
probe batches on a PS4 showed: a buffer chunk of size zero is dropped
whatever its address, and a null address with a nonzero size refuses
the batch with INVALID_ADDRESS in the error record together with the job
chunk, the chunk's offset in the job and the job's return address, with
nothing of the batch run. Inputs and outputs share that rule. The
console-measured result flags move from test comments to the debt
entries.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…he PS4 probe

The gapless entry no longer says the one-frame behaviour is unverified,
since the probe measured it, and the libSceAjmi entry names the two
exports its file declares.

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

ntamas94 commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks both. Pushed cb3e854 and 589189b; point by point:

  1. libSceAjmi stub (fogydes 1, MarcZX10 1): restored. It is declared as sceAjmBatchJobRunBufferRa_nid_disambig1, the suffix core/libs/nid and tools/nid_names.py strip before hashing: the patched libSceAjmi.prx still exports ElslOCpOIns (checked with objdump), a title importing it from libSceAjmi gets the not-implemented error at call time as on main, and the two libraries no longer define one name, which check_conventions refuses as a duplicate export. docs/dev/debt/libSceAjmi.md says so.
  2. Buffer predicates (fogydes 2, MarcZX10 2): measured on a PS4 (FW 10.71) with probe batches through the console's own builders, each with a control job before the run job:
    • a chunk of size zero is dropped whatever its address, alone or among other chunks: {ptr, 0} as the only output gives 0x10 with nothing consumed, the same as no output chunk; {ptr, 0} as the only input gives 0, the same as no input chunk; {NULL, 0} behaves the same; a zero-size chunk next to a real one changes nothing;
    • a null address with a nonzero size, input or output, first or second chunk, makes sceAjmBatchStartBuffer return 0x80930011 and fill the error record with 0x80930016, the job chunk's address, the chunk's offset in the job (24, 40 and 48 in those batches) and the job's return address, and the control job before it does not run.
      The reader now does the same: it reads the whole batch first, inputs and outputs share one predicate, a refused job runs nothing, and the error record is filled as measured. Debt entry "sceAjmBatchStartBuffer refusals"; test TestConsoleBufferEdges.
  3. Test comments (fogydes): the // comments in GuestAjm.cpp are gone. The 0x8/0x10/0x18 rules and the stale header bits were already in the debt entries, which are their home.
  4. Scope (MarcZX10 3): What, Tested and Unblocks now name the one title observed, Knowledge is Power: Decades (CUSA12435). its Game bring-up discussion is Knowledge is Power: Decades (CUSA12435) #9718, now linked from Unblocks.
  5. Order with fix(ajm): bound private batch job reads before execution #9483 and feat(libSceAjm): parse CRC-protected and non-stereo MP3 metadata frames #8468 (MarcZX10): both are older and smaller than this, so I suggest they land first; I rebase this one the same day (the overlap is the three lines closing sceAjmBatchStart's loop and one debt line, both trivial). If you would rather merge this first, those two rebase in minutes as well.

AI-assisted: yes (Claude Code)

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the follow-up evidence. The latest head addresses my earlier findings: the libSceAjmi throwing export is restored with NID disambiguation, and the buffer edge rules now match the reported PS4 probes. The title is linked to the correct Game bring-up discussion, and the current-main conventions check and git diff --check pass.

One new correctness issue: sceAjmBatchStartBuffer allows (batch == nullptr, batch_size == 0) past its parameter check, then evaluates batch + batch_size. Pointer arithmetic on a null pointer is undefined even for an offset of zero. Please either reject a null batch explicitly or avoid forming the end pointer unless batch is nonnull, and add a regression for the chosen empty-batch contract.

Before merge, the current base also needs updating: this PR targets 4a17c71, 62 commits behind main at 8b3a6ba. It still overlaps open #9483 in Ajm.cpp and #8468 in the AJM debt file; CONTRIBUTING.md requires agreeing with those authors on landing order and recording any dependency, while the checklist currently says Depends on: none. Please coordinate and correct the checklist before the rebase/merge.

Validation on this Linux host: exact-head git diff --check and tools/check_conventions.py against current main pass. I attempted a focused full configure, but it stops before compilation because this host has no SDL2 development package and the SDL2 submodule is not initialized; no local guest_ajm run is claimed. Hosted product jobs are skipped by path filters. The author reports Windows guest_ajm/vibration tests and PS4 FW 10.71 probes; those are not independently reproduced here.

ntamas94 and others added 2 commits October 22, 2026 15:25
sceAjmBatchStartBuffer let a null batch of zero bytes through its
parameter check and formed the end pointer from it. A null batch now
returns INVALID_PARAMETER whatever the size, as the PS5 builders'
sceAjmBatchInitialize does; a batch of zero bytes still starts, runs
nothing and completes at once. A test covers both; neither was measured
on a console, which the debt entry says.

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

ntamas94 commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks. Pushed ef01ffa (on top of the main merge 29364e1):

  1. Null batch: sceAjmBatchStartBuffer now rejects a null batch address with 0x80930005 whatever the size, the PS5 builders' answer for a null buffer (sceAjmBatchInitialize), so the end pointer is only formed from a real address. A batch of zero bytes keeps its contract: it starts, runs nothing and completes at once. TestConsoleEmptyBatch covers a null batch with size 0 and 16, a null batch id, and the empty batch's start, wait and second wait. I tried to measure both on the PS4 with the same probe harness as before; the console itself froze at the null-batch call before a result was written and had to be power-cycled, so neither the null nor the empty batch is measured on a console, and I will not retry that one on hardware. The debt entry "sceAjmBatchStartBuffer null and empty batches" says what is measured and what is not.
  2. Base: main at 830ccd121f is merged into the branch (no Ajm file changed on main since 4a17c71); guest_ajm, guest_ajm_vibration and check_conventions pass on it.
  3. Order: proposed in fix(ajm): bound private batch job reads before execution #9483 and feat(libSceAjm): parse CRC-protected and non-stereo MP3 metadata frames #8468 that they land first and I rebase the same day; the checklist now says Depends on: #9483, #8468. If either author prefers the other order, I will switch.

AI-assisted: yes (Claude Code)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions github-actions Bot added docs prx needs rebase Conflicts with main, rebase needed labels Oct 22, 2026
…tch-buffer

# Conflicts:
#	docs/dev/debt/libSceAjm.native.md
@ntamas94

Copy link
Copy Markdown
Contributor Author

#8468 landed, so main (3bbd2321de) is merged into the branch as 77c1377. The one conflict was the debt hunk beside the two sceAjmDecMp3ParseFrame entries: #8468's rewritten lines are kept and this PR's batch entries sit below the sceAjmBatchJobGetInfo line; no entry was changed by both. guest_ajm, guest_ajm_vibration and check_conventions pass on the merged head. The checklist now says Depends on: #9483 only, per the order agreed with @JJenkx there; when #9483 lands I merge again and keep its bounds checks in sceAjmBatchStart's loop with RegisterBatch after it.

AI-assisted: yes (Claude Code)

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 22, 2026
ntamas94 and others added 3 commits October 23, 2026 01:09
…ULTIPLE_FRAMES

A probe on a PS4 decoded exactly one frame and reported 0 for an ATRAC9
run job without RUN_MULTIPLE_FRAMES. Jobs read from a PS4 batch buffer
now do the same. Jobs from the PS5 builders keep decoding their whole
input, which FMOD in PPSA29141 relies on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ntamas94

Copy link
Copy Markdown
Contributor Author

One measured behaviour I had left as a debt note is now implemented (50838e6): an ATRAC9 run job without RUN_MULTIPLE_FRAMES read from a PS4 batch buffer decodes exactly one frame and reports 0, as the PS4 probe showed (one frame, 62 bytes consumed and 512 written on the title's stream). Jobs from the PS5 builders keep decoding their whole input, which FMOD in PPSA29141 relies on, so TestAt9RunDecodesWholeInput is unchanged; TestConsoleSingleFrameRun covers the PS4 path. main at 7e69be6793 is merged in (6a6f73b); guest_ajm, guest_ajm_vibration and check_conventions pass.

AI-assisted: yes (Claude Code)

@adriadam10 adriadam10 self-assigned this Oct 23, 2026
@MarcZX10

Copy link
Copy Markdown
Contributor

One remaining issue in TestConsoleEmptyBatch: the final successful “empty batch” call passes words (a non-null address) with batch_size == 0, so it does not test (batch == nullptr, batch_size == 0) and cannot verify the null-pointer arithmetic fix. The null+zero call above only verifies the implementation rejects it. Please either remove the claim that a zero-byte batch is accepted, or add a test using valid non-null storage with size zero and describe that as the empty-batch contract. If null+zero acceptance is intended, the implementation must avoid forming batch + batch_size when batch is null and that exact pair should be tested; the current code explicitly rejects it. The implementation's reject-null behavior itself is safe, and this is a regression-test/description mismatch rather than a remaining null arithmetic path.

I independently checked the current PR head 6a6f73bd9d70ca52c2c78055f1b9b0b98197a4ba: the null check precedes end-pointer formation, git diff --check is clean, hosted conventions and labels pass, and product build/test jobs are skipped by path filters. I did not rerun guest_ajm locally. #9483 remains open and is still correctly listed as a dependency.

…dress

A null batch address is refused whatever the size, so the accepted batch
of zero bytes is one at a real address, which is what TestConsoleEmptyBatch
starts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ntamas94

Copy link
Copy Markdown
Contributor Author

@MarcZX10 Thanks, agreed: the description was the problem, not the code. The contract is the second option you describe. A null batch address is refused with 0x80930005 whatever the size, including size 0, and that is what the two nullptr calls at the top of TestConsoleEmptyBatch check. The accepted empty batch is zero bytes at a real address, and the last call starts exactly that (words, size 0), then checks the id, the completed wait and the second wait. No null-plus-zero path is accepted, so batch + batch_size is only ever formed from a non-null address.

Pushed d924eb2, which says this in the debt entry ("a batch of zero bytes at a non-null address is accepted … a null address with size 0 is refused like any null address"); the Tested line in the description now says the same. main 5e6ae6e is merged in first (e8969a2, no conflict). guest_ajm and guest_ajm_vibration pass 3 of 3 on the head, and check_conventions.py --base origin/main passes.

AI-assisted: yes (Claude Code)

@MarcZX10 MarcZX10 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The previous findings are addressed on the current head: null batch storage is rejected before end-pointer formation, the empty-batch test uses valid non-null storage, and the debt/description now state that contract consistently. The branch merges cleanly with current main, and conventions, labels and report pass. Product build/test jobs are skipped by repository path filters; the author reports guest_ajm and guest_ajm_vibration pass 3/3 on this head, which I did not independently rerun. #9483 remains correctly listed as a dependency and must land first per the agreed order.

danielradosa added a commit to danielradosa/AnyPS5 that referenced this pull request Oct 24, 2026
…s in the console's chunk layout

Took the PR's Ajm.cpp buffer path, ATRAC9 run-result/bitrate changes and the libSceAjm stub removal / libSceAjmi disambig as-is; kept our test declarations and debt bullets (DecodeSplit, codec info, Encode, helpers) beside the PR's, GetInfo and gapless bullets take the PR's wording.
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 24, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs needs rebase Conflicts with main, rebase needed prx unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants