Merge pull request #3824 from xXPBSXx/fix/ces-cp1251-profile

feat(libSceCesCs): add sceCesRefersUcsProfileCp1251 and sceCesMbcsUcsContextInit
This commit is contained in:
Dean 2026-10-12 21:36:23 +13:00 committed by GitHub
commit 773f8de7b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 72 additions and 8 deletions

View File

@ -13,18 +13,45 @@ constexpr int CES_ERROR_UNASSIGNED_CODE = static_cast<int>(0x805C0020);
constexpr int CES_ERROR_INVALID_DST_BUFFER = static_cast<int>(0x805C0030);
constexpr int CES_ERROR_DST_BUFFER_END = static_cast<int>(0x805C0031);
constexpr uint8_t CP1252_PROFILE = 0;
constexpr uint8_t PROFILES[2] = {0, 1};
constexpr const uint8_t* CP1252_PROFILE = &PROFILES[0];
constexpr const uint8_t* CP1251_PROFILE = &PROFILES[1];
constexpr uint16_t CP1252_HIGH[32] = {
0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F,
0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178,
};
uint32_t cp1252ToUnicode(uint8_t sbc) {
constexpr uint16_t CP1251_HIGH[128] = {
0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x0098, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457,
0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
};
uint32_t sbcToUnicode(const uint8_t* profile, uint8_t sbc) {
if (profile == CP1251_PROFILE) return sbc >= 0x80 ? CP1251_HIGH[sbc - 0x80] : sbc;
return sbc >= 0x80 && sbc < 0xA0 ? CP1252_HIGH[sbc - 0x80] : sbc;
}
bool unicodeToCp1252(uint32_t code, uint8_t& sbc) {
bool unicodeToSbc(const uint8_t* profile, uint32_t code, uint8_t& sbc) {
if (profile == CP1251_PROFILE) {
if (code < 0x80) {
sbc = static_cast<uint8_t>(code);
return true;
}
for (uint32_t i = 0; i < 128; ++i) {
if (CP1251_HIGH[i] == code) {
sbc = static_cast<uint8_t>(0x80 + i);
return true;
}
}
return false;
}
if (code < 0x80 || (code >= 0xA0 && code <= 0xFF)) {
sbc = static_cast<uint8_t>(code);
return true;
@ -81,15 +108,25 @@ uint32_t encodeUtf8(uint32_t code, uint8_t* utf8) {
extern "C" {
const uint8_t* APS5_VABI sceCesRefersUcsProfileCp1252(void) {
return &CP1252_PROFILE;
return CP1252_PROFILE;
}
const uint8_t* APS5_VABI sceCesRefersUcsProfileCp1251(void) {
return CP1251_PROFILE;
}
int APS5_VABI sceCesMbcsUcsContextInit(void* context, const void* profile) {
(void)profile;
if (!context) return CES_ERROR_INVALID_PARAMETER;
return 0;
}
int APS5_VABI sceCesSbcToUtf8(const uint8_t* profile, uint8_t sbc, uint8_t* utf8, uint32_t utf8max, uint32_t* utf8_len) {
if (profile != &CP1252_PROFILE) return CES_ERROR_INVALID_PARAMETER;
if (profile != CP1252_PROFILE && profile != CP1251_PROFILE) return CES_ERROR_INVALID_PARAMETER;
if (!utf8) return CES_ERROR_INVALID_DST_BUFFER;
uint8_t encoded[3];
const uint32_t length = encodeUtf8(cp1252ToUnicode(sbc), encoded);
const uint32_t length = encodeUtf8(sbcToUnicode(profile, sbc), encoded);
if (utf8max < length) return CES_ERROR_DST_BUFFER_END;
for (uint32_t i = 0; i < length; ++i) utf8[i] = encoded[i];
if (utf8_len) *utf8_len = length;
@ -97,7 +134,7 @@ int APS5_VABI sceCesSbcToUtf8(const uint8_t* profile, uint8_t sbc, uint8_t* utf8
}
int APS5_VABI sceCesUtf8ToSbc(const uint8_t* utf8, uint32_t utf8max, uint32_t* utf8_len, const uint8_t* profile, uint8_t* sbc) {
if (profile != &CP1252_PROFILE) return CES_ERROR_INVALID_PARAMETER;
if (profile != CP1252_PROFILE && profile != CP1251_PROFILE) return CES_ERROR_INVALID_PARAMETER;
if (!utf8) return CES_ERROR_INVALID_SRC_BUFFER;
if (utf8max == 0) return CES_ERROR_SRC_BUFFER_END;
if (!sbc) return CES_ERROR_INVALID_DST_BUFFER;
@ -105,7 +142,7 @@ int APS5_VABI sceCesUtf8ToSbc(const uint8_t* utf8, uint32_t utf8max, uint32_t* u
uint32_t length = 0;
uint32_t code = 0;
if (const int result = decodeUtf8(utf8, utf8max, length, code); result != 0) return result;
if (!unicodeToCp1252(code, *sbc)) return CES_ERROR_UNASSIGNED_CODE;
if (!unicodeToSbc(profile, code, *sbc)) return CES_ERROR_UNASSIGNED_CODE;
if (utf8_len) *utf8_len = length;
return 0;
}

View File

@ -5,6 +5,8 @@
extern "C" {
const std::uint8_t* APS5_VABI sceCesRefersUcsProfileCp1252(void);
const std::uint8_t* APS5_VABI sceCesRefersUcsProfileCp1251(void);
int APS5_VABI sceCesMbcsUcsContextInit(void*, const void*);
int APS5_VABI sceCesSbcToUtf8(const std::uint8_t*, std::uint8_t, std::uint8_t*, std::uint32_t, std::uint32_t*);
int APS5_VABI sceCesUtf8ToSbc(const std::uint8_t*, std::uint32_t, std::uint32_t*, const std::uint8_t*, std::uint8_t*);
}
@ -80,4 +82,28 @@ int main() {
Require(Utf8ToSbc(profile, "\xED\xA0\x80", 3) == invalidEncode);
Require(Utf8ToSbc(profile, "\xE3\x81\x82", 3) == unassignedCode);
Require(Utf8ToSbc(profile, "\xF0\x9F\x98\x80", 4) == unassignedCode);
const std::uint8_t* cyrillic = sceCesRefersUcsProfileCp1251();
Require(cyrillic != nullptr && cyrillic == sceCesRefersUcsProfileCp1251() && cyrillic != profile);
RequireSbcToUtf8(cyrillic, 'A', "A");
RequireSbcToUtf8(cyrillic, 0x88, "\xE2\x82\xAC");
RequireSbcToUtf8(cyrillic, 0xC0, "\xD0\x90");
RequireSbcToUtf8(cyrillic, 0xFF, "\xD1\x8F");
RequireSbcToUtf8(cyrillic, 0xA8, "\xD0\x81");
RequireSbcToUtf8(cyrillic, 0xB9, "\xE2\x84\x96");
RequireUtf8ToSbc(cyrillic, "\xD0\x9F", 0xCF);
RequireUtf8ToSbc(cyrillic, "\xD1\x91", 0xB8);
for (int sbc = 1; sbc < 256; ++sbc) {
std::uint8_t cp[4] = {};
std::uint32_t cpLength = 0;
std::uint8_t back = 0;
Require(sceCesSbcToUtf8(cyrillic, static_cast<std::uint8_t>(sbc), cp, sizeof(cp), &cpLength) == 0);
Require(sceCesUtf8ToSbc(cp, cpLength, &cpLength, cyrillic, &back) == 0 && back == sbc);
}
Require(Utf8ToSbc(cyrillic, "\xC3\xA9", 2) == unassignedCode);
Require(Utf8ToSbc(profile, "\xD0\x90", 2) == unassignedCode);
char context[256] = {};
Require(sceCesMbcsUcsContextInit(context, cyrillic) == 0);
Require(sceCesMbcsUcsContextInit(nullptr, cyrillic) == invalidParameter);
}

View File

@ -40,6 +40,7 @@ Throughout the project, every function at every stage either **does exactly what
- [libSceHttp2](../../core/libs/prx/libSceHttp2/Export.cpp) - no request reaches the network, so `sceHttp2SetMinSslVersion` has no TLS connection to restrict and its value is ignored, as for `sceHttp2SslEnableOption` and `sceHttp2SslDisableOption`. The three setters validate `id` against the live handle registry and return `SCE_HTTP2_ERROR_INVALID_ID` (`0x817B1100`) for a handle that was never allocated, was deleted, or belonged to a terminated context, following [prosper](https://github.com/mattias800/prosper). Whether the console restricts the object kind these setters accept, and what it does with an id from a previous init/term cycle, is unverified, so any live handle kind is accepted and ids are never reused. `sceHttp2Term`, `sceHttp2DeleteTemplate`, `sceHttp2DeleteRequest`, `sceHttp2CreateTemplate`, `sceHttp2CreateRequestWithURL`, `sceHttp2CreateCookieBox` and `sceHttp2SendRequestAsync` also return the invalid-id error for a handle of the wrong kind or no longer live, following prosper (not measured on a console)
- [libSceNpTrophy2](../../core/libs/prx/libSceNpTrophy2/src/GameInfo.cpp): `sceNpTrophy2RegisterUnlockCallback` accepts a callback that is never called, as no trophy is ever unlocked, `sceNpTrophy2UnregisterUnlockCallback` only returns success, and `sceNpTrophy2ShowTrophyList` returns success without showing a list (there is no system trophy UI; seen on PPSA15552).
- [sceAvPlayerSetTrickSpeed](../../core/libs/prx/libSceAvPlayer/src/Source.cpp) (libSceAvPlayer) with a negative speed runs the clock backwards but delivers no frames; when a forward speed is set again, playback resumes from the rewound time.
- [sceCesMbcsUcsContextInit](../../core/libs/prx/libSceCesCs/Export.cpp) (libSceCesCs) - returns success without writing to the context, and returns the invalid-parameter error for a null context. The signature `int sceCesMbcsUcsContextInit(union* context, const union* mbcsUcsPrf)` follows the [Orbital IDA type database](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json); the context layout and its size are unknown, so nothing is stored. PPSA15246 calls it and imports no other function that takes the context; no conversion through an initialized context exists. `sceCesRefersUcsProfileCp1251` returns a profile accepted by `sceCesSbcToUtf8` and `sceCesUtf8ToSbc` only, with the Windows-1251 table of Microsoft's [bestfit1251.txt](https://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WindowsBestFit/bestfit1251.txt), where byte 0x98 (unassigned) maps to U+0098 like the unassigned Cp1252 bytes; the console behavior for that byte is not verified
- [sceRudpProcessEvents and sceRudpNetFlush](../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - there is no reliable-UDP stack on the host, and every `sceRudpWrite`, `sceRudpRead` and `sceRudpGetStatus` call here reports a reset or idle context without queueing data, so no context ever holds pending data to process or send; both calls return success after `sceRudpInit` and `RUDP_ERROR_NOT_INITIALIZED` before it, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/rudp/rudp.cpp) returns OK from its stubs. PPSA03490 calls `sceRudpProcessEvents(0)` followed by `sceRudpNetFlush()` from its P2P service thread and ignores the first result; whether the real calls take parameters is unknown
- [sceRudpActivate](../../core/libs/prx/libSceRudp/Export.cpp) (libSceRudp) - there is no reliable-UDP stack on the host to activate, so it returns success without doing anything, as [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/rudp/rudp.cpp) does for its own `sceRudpActivate`. PPSA01325 calls it with no arguments during start-up; what the console activates, and whether it reports an error before `sceRudpInit`, is unverified
- [ulobjmgr](../../core/libs/prx/ulobjmgr/Export.cpp) registers no object: `_sceUlobjmgrRegisterObject` always hands out id 0 and `_sceUlobjmgrUnregisterObject` releases nothing, as shadPS4 does