Skip to content

feat(libSceCesCs): implement the GB four-byte range and user-defined area setters - #13573

Open
KernelPanicNT wants to merge 4 commits into
boykopovar:mainfrom
KernelPanicNT:feat/cescs-gb-profile-setters
Open

KernelPanicNT wants to merge 4 commits into
boykopovar:mainfrom
KernelPanicNT:feat/cescs-gb-profile-setters

Conversation

@KernelPanicNT

@KernelPanicNT KernelPanicNT commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

What

Source of the behaviour

Measured with a homebrew probe running as a payload process on a device (firmware 13.00). The probe loaded the system libSceCesCs.sprx, built the four GB profiles in its own guarded static sheets, called each setter on each profile with 17 values (0-8, 15, 16, 0x100, 0x10000, INT_MAX, -1, -2, INT_MIN) after restoring the sheet byte for byte, and recorded the result and every changed sheet byte. For each accepted value 0-8 that left a distinct sheet, it then ran sceCesGbToUtf32 on 28 fixed byte sequences and sceCesUtf32ToGb on 26 fixed code points and logged result, size and output. No guard, sheet or buffer outside the expected one changed and no call faulted.

Case Device AnyPS5 now
Set4ByteCharRange, GB 18030-2000 and -2005 sheets, values 0, 1, 2, 3, 5, 7 0 same
Same, values 4, 6, 8, 15, 16, 0x100, 0x10000, INT_MAX, -1, -2, INT_MIN 0x805C0001, sheet unchanged same; every other value outside the six is taken as invalid
Set4ByteCharRange on GBK and EUC-CN sheets (all 17 values), null profile (0 and 1) 0x805C0004, sheet unchanged same
SetUdaMapping, GB 18030 and GBK sheets, 0 and 1 0 same
Same, the other 15 values 0x805C0001, sheet unchanged same; every value other than 0 and 1 is taken as invalid
SetUdaMapping on EUC-CN (all 17 values), null profile (0 and 1) 0x805C0004, sheet unchanged same
Sheet bytes the edition's default (range 1 for 2000, 3 for 2005; mapping 1 for GB 18030, 0 for GBK) changes nothing; otherwise the range sets the high nibble of byte 4 to 0x0, 0x1, 0x2, 0x3, 0x7 or 0xF for ranges 0, 1, 2, 3, 5 and 7, and byte 5 to 0x04 for ranges 0 and 2 and 0x02 for 7, and the mapping toggles bit 0x04 of byte 4 the setting is kept in sheet bytes 8 (range) and 9 (mapping), after the 8-byte tag; the default changes nothing, any other accepted value changes that one byte. The console's layout is not reproduced
Ranges 0 and 2, both editions: 81308130, 8135F437, 8431A439 0x805C0020, size 4 same for every four-byte sequence of a BMP code point
Same: U+0080, U+1E3F (2000) or U+E7C7 (2005), U+FE10, U+FFFF to GB 0x805C0020, size 0 same for every BMP code point without a two-byte cell
Range 3 on 2000, range 1 on 2005 the 54 results of the default sheet same
Range 5 90308130 and U+10000 convert (with gbMax 2 or 3, 0x805C0031 and size 4); E3329A35 and U+10FFFF 0x805C0020 same
Range 7 as 5, and E3329A35 and U+10FFFF convert same
8431A530, E3329A36, FE39FE39 0x805C0020, size 4, with every range same
Short and invalid sequences (81, 8130, 813081, 81308130 with gbMax 2, 817F, 80, FF), two-byte cells outside the user-defined areas unchanged by either setter same
Mapping 0 on GB 18030: AAA1, AFFE, F8A1, FEFE, A140, A7A0; U+E000, U+E233, U+E234, U+E4C5, U+E4C6, U+E5E5 0x805C0020 (size 2 decoding, 0 encoding); A6D9 U+E78D and FE51 U+E816 still convert same for every cell that decodes to U+E000-U+E765 (the three user-defined areas)
Mapping 1 on GBK A140 U+E4C6, A7A0 U+E765, F8A1 U+E234, FEFE U+E4C5; AAA1 and AFFE stay 0x805C0020; the six code points above still encode to 0x805C0020 same for every cell of the A140-A7A0 and F8A1-FEFE areas; encoding unchanged
Supplementary characters other than U+10000 and U+10FFFF (plane 2 included) with a range other than the edition's default not measured throws
A profile of another family (for example Shift-JIS) not measured throws

Implements sceCesGbUcsProfileSet4ByteCharRange(void* profile, int range) and sceCesGbUcsProfileSetUdaMapping(void* profile, int udaMapping). Both called NotImplemented_nid_no_patch.

The inits now write the edition's default range and mapping into sheet bytes 8 and 9, and the setters validate and store a new value there. The accepted ranges and what each does to the measured samples are one table in Export.cpp; the GB 18030 decoder and encoder consult it for four-byte forms, and both GB 18030 directions and the GBK decoder consult the mapping for the user-defined areas. With the default range the supplementary mapping is unchanged (none for 2000, plane 2 for 2005). With any other range, U+10000 and U+10FFFF follow the measurement and every other supplementary character throws std::runtime_error, because the probe did not reach plane 2 or the middle of the range (ranges 0 and 2 answer alike on every sample, so what 2 adds is not known). What is generalised from the samples is listed in a new docs/dev/debt/libSceCesCs.md entry: the whole four-byte BMP range, every cell of an area, values outside the 17 tried, the other conversion forms (ToUcs2, ToUtf8, ToUtf16, the string conversions), which share the decoder and encoder, and the two setters combined. The GBK entry now names only sceCesGbUcsProfileSetSbcs as unhandled, the GB 18030 entry drops the sentence about these two throwing, and the pending-export count goes from 17 to 15.

Open questions and overlap

  • On the device the EUC-CN sheet decodes 0x80-0xA0 as single bytes (80 U+0080, 81308130 U+0081 with size 1, 817F U+0081); here those leads answer 0x805C0014. The setters reject EUC-CN, so this PR does not change it.
  • #10431 moves the GB tags to seven characters and keeps an SetSbcs selection in the tag's eighth byte; it leaves these two exports as stubs. This PR stores its settings after the tag (bytes 8 and 9), so the two designs fit together, but the init functions, PendingExports.cpp, Api.hpp and the debt file conflict textually.
  • #13140 only declares sceCesUcsProfileInitGb18030_2022 next to these stubs (still declaration-only); #12946 edits the same CMake variant list; #9912, #11614 and #15409 remove other pending stubs and change the same pending-count line, which #14085 and #14212 also rewrite.
  • tools/pr_overlap.py --branch feat/cescs-gb-profile-setters: no open PR implements these exports. #9912, #11614, #12946, #13486, #14212 and #15409 conflict with main, so they were compared by hand; none adds these two functions.

Tested

Linux (GCC). The new test runs as guest_ces_gbsetters and guest_ces_gbsetters_module (linked against libSceCesCs and libSceCesCs-module). It calls both setters with the 17 values on the four profiles and a null profile and checks every result, and that the sheet changes exactly when the device's did. For the 16 measured sheet states it runs the 28 decode and 26 encode samples and compares an FNV-1a hash of (result, size, output) with the hash of the device results; it also checks samples from the table and that the unmeasured supplementary characters and a Shift-JIS profile throw. All 24 guest_ces tests pass on main (b11c7bf84f) with this branch applied. The new tests fail with the two setters reduced to NotImplemented_nid_no_patch stubs (both abort, checked at this head); at f785292f5d they also failed with Export.cpp, PendingExports.cpp and Api.hpp from main (they do not build) and with setters that store the value while the conversions ignore it (both abort, while guest_ces_chinese and guest_ces_mbc still pass). tools/progress.py: +2 implemented functions (libSceCesCs 270/287 -> 272/287). tools/check_conventions.py passes.

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: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

…area setters

sceCesGbUcsProfileSet4ByteCharRange accepts ranges 0, 1, 2, 3, 5 and 7
on the GB 18030 profiles and sceCesGbUcsProfileSetUdaMapping accepts 0
and 1 on the GB 18030 and GBK profiles, as measured as a payload process
on a device. Other values return 0x805C0001; a null, GBK (range) or
EUC-CN profile returns 0x805C0004. The setting is kept in the sheet and
read by the GB conversions: ranges 0 and 2 drop the four-byte BMP forms,
5 adds U+10000 and 7 adds U+10FFFF; mapping 0 makes the GB 18030
user-defined cells unassigned and mapping 1 lets GBK decode the
F8A1-FEFE and A140-A7A0 areas. Supplementary characters the measurement
did not cover throw for a non-default range.
@github-actions

github-actions Bot commented Oct 30, 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 9912 core/libs/prx/libSceCesCs/Export.cpp
core/libs/prx/libSceCesCs/PendingExports.cpp
docs/dev/debt/libSceCesCs.md
PR 10431 core/libs/prx/libSceCesCs/CMakeLists.txt
core/libs/prx/libSceCesCs/Export.cpp
core/libs/prx/libSceCesCs/PendingExports.cpp
core/libs/prx/libSceCesCs/include/Api.hpp
docs/dev/debt/libSceCesCs.md
PR 10751 core/libs/prx/libSceCesCs/Export.cpp
core/libs/prx/libSceCesCs/PendingExports.cpp
core/libs/prx/libSceCesCs/include/Api.hpp
docs/dev/debt/libSceCesCs.md
PR 11614 core/libs/prx/libSceCesCs/Export.cpp
core/libs/prx/libSceCesCs/PendingExports.cpp
core/libs/prx/libSceCesCs/include/Api.hpp
docs/dev/debt/libSceCesCs.md
PR 13140 core/libs/prx/libSceCesCs/PendingExports.cpp
core/libs/prx/libSceCesCs/include/Api.hpp
docs/dev/debt/libSceCesCs.md
PR 14085 docs/dev/debt/libSceCesCs.md
PR 14212 docs/dev/debt/libSceCesCs.md
PR 14284 core/libs/prx/libSceCesCs/CMakeLists.txt

@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Progress report (0263899 → 5818cdd)

📈 System libraries: 26.7% (+0.01%, +2 implemented functions)

✅ 2 implemented
Library Name
libSceCesCs sceCesGbUcsProfileSet4ByteCharRange
libSceCesCs sceCesGbUcsProfileSetUdaMapping

Changed libraries (implemented exports)

Library Before After Change
libSceCesCs 270/287 (94.08%) 272/287 (94.77%) +0.70%

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@KernelPanicNT Reviewed at f785292f5d. Please name the firmware version used for the payload measurement in the PR and the new setters' debt entry. The probe description distinguishes sampled results from generalizations, but currently says only “a device.” The repository's docs/dev/REVERSE_ENGINEERING.md, “Sources, strongest first,” calls for a console measurement with its firmware version. This will pin the source of the six accepted range values, mapping values and conversion results without implying they were checked on other firmware.

@blawesom

Copy link
Copy Markdown
Contributor

Verified f785292f5d on Linux x86-64 (Ryzen 7 9800X3D, CachyOS kernel 6.2.10, GCC 16.2.1, Release, BUILD_TESTING=ON, Ninja).

Build

  • guest_ces_gbsetters_tests and guest_ces_gbsetters_module_tests build clean at the PR head.

Tests (all at the PR head)

  • ctest -R '^guest_ces_gbsetters(_module)?$': 2/2 pass, plus 5 repeats for flakiness: pass every time; also 3 direct runs of each binary: exit 0 every time.
  • Whole family ctest -R '^guest_ces': 22/22 pass (the pre-existing guest_ces tests are unaffected by the decode/encode signature changes).
  • Not run: anything needing a title, Windows, or a console; the device probe itself obviously can't be re-run here, so the measured hashes are trusted as test fixtures, not re-measured.

Code reading

  • PendingExports.cpp: both stubs removed, no duplicates left behind; debt entry pending count updated 26 → 24 correctly. Api.hpp declarations now match the implemented signatures.
  • Ranges table (0, 1, 2, 3, 5, 7) matches the PR body's measured acceptance and the default-range supplementary mapping is kept for the edition default (1 for 2000, 3 for 2005); non-default ranges throw for supplementary code points other than U+10000/U+10FFFF, which the body marks as not measured and the debt entry records — consistent with the repo's unknown-behaviour convention.
  • UDA handling: U+E000–U+E765 gate on sheet byte 9 for GB 18030 both directions, GBK decode derives user-defined values via gb18030Cell (E234–E765 within the measured area), GBK encode stays unassigned per the body's table. Test covers valid and invalid code points, both setters' valid/invalid values, all four profiles, null profiles, sheet-change-vs-device comparison, and the throw paths.
  • No code comments; conventions followed.

Open

  • @oneandonlydean's review above (name the firmware version of the payload measurement in the PR body and the new debt entry) is not yet addressed; the debt entry still says only "a device". With that resolved this looks approve-worthy to me.

@adriadam10 adriadam10 self-assigned this Oct 31, 2026

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed at f785292f5d: the setters, the range table, the sheet bytes 8/9, the decoder/encoder changes, the throwing paths and the debt entries match the description, and the test pins the measured hashes (it aborts with the setters as stubs).

One thing left, the one @oneandonlydean asked for above and not yet done:

  • State the firmware version of the payload measurement in the PR body and in the sceCesGbUcsProfileSet4ByteCharRange and sceCesGbUcsProfileSetUdaMapping entry of docs/dev/debt/libSceCesCs.md (line 18), as docs/dev/REVERSE_ENGINEERING.md ("Sources, strongest first") requires for a console measurement. Other entries in that file say it as "(firmware 13.60)".

With that added this is good to merge.

KernelPanicNT added 2 commits November 4, 2026 14:03
…-setters

# Conflicts:
#	docs/dev/debt/libSceCesCs.md
The sceCesGbUcsProfileSet4ByteCharRange and
sceCesGbUcsProfileSetUdaMapping entry now states that the ranges,
mappings and conversion results come from a payload process on a
device (firmware 13.00).
@KernelPanicNT

Copy link
Copy Markdown
Contributor Author

Thanks @oneandonlydean for pointing to "Sources, strongest first" and asking for the firmware, and @adriadam10 for the careful review and for spelling out exactly where it was missing.

Done in 365d547: the sceCesGbUcsProfileSet4ByteCharRange and sceCesGbUcsProfileSetUdaMapping entry in docs/dev/debt/libSceCesCs.md (still line 18) now says the ranges, mappings and conversion results were measured as a payload process on a device (firmware 13.00), and the PR body's source block says the same. That is the only firmware the probe ran on, so neither text implies the values were checked on other versions.

The conflict with main is resolved: only the pending-export count in the debt file clashed, and it now reads 15 (main's 17 minus these two). The setter tests and the rest of guest_ces (24/24) still pass, and the new tests still abort with the two setters reduced to stubs.

Thanks also @blawesom for the independent run and code read.

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Nov 4, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Nov 4, 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

build docs needs rebase Conflicts with main, rebase needed prx

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants