Merge pull request #2981 from MarcZX100/codex/system-service-language-2769
feat(libSceSystemService): select the console language
This commit is contained in:
commit
59a8cbab89
|
|
@ -29,6 +29,15 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(guest_system_service PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_test_executable(guest_system_service_language_tests ${LIBS_INCLUDE_DIR}/tests/GuestSystemServiceLanguage.cpp)
|
||||
target_include_directories(guest_system_service_language_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_system_service_language_tests PRIVATE libSceSystemService libc)
|
||||
configure_windows_unwind(guest_system_service_language_tests)
|
||||
add_test(NAME guest_system_service_language COMMAND guest_system_service_language_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_system_service_language PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_test_executable(guest_system_kill_app_tests ${LIBS_INCLUDE_DIR}/tests/GuestSystemKillApp.cpp)
|
||||
target_include_directories(guest_system_kill_app_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_system_kill_app_tests PRIVATE libSceSystemService libc)
|
||||
|
|
|
|||
|
|
@ -2,12 +2,35 @@
|
|||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <charconv>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include "prx/libc/include/Shutdown.hpp"
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libkernel/KernelErrors.hpp"
|
||||
#include "prx/libSceSystemService/SystemService.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
int ConsoleLanguage() {
|
||||
const char* configured = std::getenv("ANYPS5_LANGUAGE");
|
||||
if (configured == nullptr || configured[0] == '\0') return SYSTEM_SERVICE_PARAM_LANG_ENGLISH_US;
|
||||
const std::string_view text(configured);
|
||||
int language = -1;
|
||||
const auto [end, error] = std::from_chars(text.data(), text.data() + text.size(), language);
|
||||
if (error != std::errc{} || end != text.data() + text.size() || language < SYSTEM_SERVICE_PARAM_LANG_JAPANESE || language > SYSTEM_SERVICE_PARAM_LANG_UKRAINIAN) {
|
||||
throw std::runtime_error("sceSystemServiceParamGetInt: ANYPS5_LANGUAGE must be a console language number from " +
|
||||
std::to_string(SYSTEM_SERVICE_PARAM_LANG_JAPANESE) + " to " + std::to_string(SYSTEM_SERVICE_PARAM_LANG_UKRAINIAN) +
|
||||
", not \"" + std::string(text) + "\"");
|
||||
}
|
||||
return language;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceSystemServiceLoadExec(const char* path, const char* const* arguments) {
|
||||
|
|
@ -69,7 +92,7 @@ int APS5_VABI sceSystemServiceParamGetInt(int paramId, int* value) {
|
|||
return SYSTEM_SERVICE_ERROR_PARAMETER;
|
||||
}
|
||||
switch (paramId) {
|
||||
case SYSTEM_SERVICE_PARAM_ID_LANG: *value = SYSTEM_SERVICE_PARAM_LANG_ENGLISH_US; break;
|
||||
case SYSTEM_SERVICE_PARAM_ID_LANG: *value = ConsoleLanguage(); break;
|
||||
case SYSTEM_SERVICE_PARAM_ID_DATE_FORMAT: *value = SYSTEM_SERVICE_PARAM_DATE_FORMAT_DDMMYYYY; break;
|
||||
case SYSTEM_SERVICE_PARAM_ID_TIME_FORMAT: *value = SYSTEM_SERVICE_PARAM_TIME_FORMAT_24HOUR; break;
|
||||
case SYSTEM_SERVICE_PARAM_ID_TIME_ZONE: *value = 0; break;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,10 @@ constexpr int SYSTEM_SERVICE_PARAM_ID_ENTER_BUTTON_ASSIGN = 1000;
|
|||
|
||||
constexpr std::size_t SYSTEM_SERVICE_MAX_SYSTEM_NAME_LENGTH = 65;
|
||||
|
||||
constexpr int SYSTEM_SERVICE_PARAM_LANG_JAPANESE = 0;
|
||||
constexpr int SYSTEM_SERVICE_PARAM_LANG_ENGLISH_US = 1;
|
||||
constexpr int SYSTEM_SERVICE_PARAM_LANG_ARABIC = 21;
|
||||
constexpr int SYSTEM_SERVICE_PARAM_LANG_UKRAINIAN = 30;
|
||||
constexpr int SYSTEM_SERVICE_PARAM_DATE_FORMAT_DDMMYYYY = 1;
|
||||
constexpr int SYSTEM_SERVICE_PARAM_TIME_FORMAT_24HOUR = 1;
|
||||
constexpr int SYSTEM_SERVICE_PARAM_GAME_PARENTAL_OFF = 0;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include "prx/libSceSystemService/SystemService.hpp"
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
extern "C" int APS5_VABI sceSystemServiceParamGetInt(int paramId, int* value);
|
||||
|
||||
namespace {
|
||||
|
||||
void Require(bool value) { if (!value) std::abort(); }
|
||||
|
||||
void SetLanguage(const char* value) {
|
||||
#ifdef _WIN32
|
||||
Require(_putenv_s("ANYPS5_LANGUAGE", value != nullptr ? value : "") == 0);
|
||||
#else
|
||||
Require((value != nullptr ? ::setenv("ANYPS5_LANGUAGE", value, 1) : ::unsetenv("ANYPS5_LANGUAGE")) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int Language() {
|
||||
int value = -1;
|
||||
Require(sceSystemServiceParamGetInt(SYSTEM_SERVICE_PARAM_ID_LANG, &value) == SYSTEM_SERVICE_OK);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool Rejected(const char* value) {
|
||||
SetLanguage(value);
|
||||
int language = -1;
|
||||
try {
|
||||
static_cast<void>(sceSystemServiceParamGetInt(SYSTEM_SERVICE_PARAM_ID_LANG, &language));
|
||||
} catch (const std::runtime_error& error) {
|
||||
return language == -1 && std::string(error.what()).find("ANYPS5_LANGUAGE") != std::string::npos;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
SetLanguage(nullptr);
|
||||
Require(Language() == SYSTEM_SERVICE_PARAM_LANG_ENGLISH_US);
|
||||
SetLanguage("");
|
||||
Require(Language() == SYSTEM_SERVICE_PARAM_LANG_ENGLISH_US);
|
||||
SetLanguage("21");
|
||||
Require(Language() == SYSTEM_SERVICE_PARAM_LANG_ARABIC);
|
||||
SetLanguage("0");
|
||||
Require(Language() == SYSTEM_SERVICE_PARAM_LANG_JAPANESE);
|
||||
SetLanguage("30");
|
||||
Require(Language() == SYSTEM_SERVICE_PARAM_LANG_UKRAINIAN);
|
||||
for (const char* value : {"31", "-1", "+21", " 21", "21 ", "21a", "0x15", "arabic", "4294967317"}) {
|
||||
Require(Rejected(value));
|
||||
}
|
||||
|
||||
SetLanguage("arabic");
|
||||
Require(sceSystemServiceParamGetInt(SYSTEM_SERVICE_PARAM_ID_LANG, nullptr) == SYSTEM_SERVICE_ERROR_PARAMETER);
|
||||
int enterButton = -1;
|
||||
Require(sceSystemServiceParamGetInt(SYSTEM_SERVICE_PARAM_ID_ENTER_BUTTON_ASSIGN, &enterButton) == SYSTEM_SERVICE_OK);
|
||||
Require(enterButton == SYSTEM_SERVICE_PARAM_ENTER_BUTTON_CROSS);
|
||||
}
|
||||
|
|
@ -245,6 +245,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [sceUserServiceGetAgeLevel](../../core/libs/prx/libSceUserService/Export.cpp) (libSceUserService) - the meaning of the value is unknown; 0 is reported, as for the unset game presets. PPSA12544 (Unity) only stores it for its user profile
|
||||
- [sceGameUpdateGetAddcontLatestVersion](../../core/libs/prx/libSceGameUpdate/Export.cpp) (libSceGameUpdate) - a null `entitlement_label` throws: KytyPS5 accepts it, prosper returns `0x80412803`. Bytes after `found` are zeroed as in KytyPS5; prosper leaves them untouched
|
||||
- [sceSystemServiceParamGetString](../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the system name (parameter 6) is the fixed `PS5`, not the console's name; other parameters and buffers of 1 to 64 bytes throw
|
||||
- [sceSystemServiceParamGetInt](../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - console language numbers 0 to 29 follow the PS4 SDK enum in [OpenOrbis](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/06fcf7fd108fa080035cdd3259ed22eb07e0508c/include/orbis/_types/sys_service.h#L54-L83) (21 Arabic). 30 (Ukrainian) is not in it and follows PPSA99505 ([Jelly5](https://github.com/02dnot/Jelly5/blob/552b866d89b2a58b27f7f7b0484dc743ce235526/app/src/app/i18n.cpp#L29-L60)) and PPSA99002 ([ProsperoLight](https://github.com/blackbearreloaded/ProsperoLight/blob/b708bbde3457bef1073a9ab6959957cb691e8e2f/include/i18n.hpp#L252-L309)), whose numbers agree with the enum. Not confirmed on a console, and neither title starts on `main` yet. Without `ANYPS5_LANGUAGE` the language is English (United States); the date format, time format, time zone, summer time and enter button stay fixed
|
||||
- [sceNgs2SystemSetSampleRate, sceNgs2SystemSetUserData, sceNgs2SystemGetUserData, sceNgs2SystemLock, sceNgs2SystemUnlock](../../core/libs/prx/libSceNgs2.native/src/System.cpp) (libSceNgs2.native) - shadPS4 only checks the system handle (`INVALID_SYSTEM_HANDLE`) and SharpProspero names what each call does; neither gives the other limits. A sample rate of 0 and a null user data pointer throw, any other rate is accepted, and changing the rate while a sampler filter is enabled throws because the filter coefficients were computed for the old rate. Lock and unlock take the same lock as `sceNgs2RackLock`
|
||||
- [sceNgs2RackGetInfo](../../core/libs/prx/libSceNgs2.native/src/Rack.cpp) (libSceNgs2.native) - field order from [shadPS4](https://github.com/shadps4-emu/shadPS4)'s `OrbisNgs2RackInfo` with the 64-byte name of the other info structures (216 bytes); `type` is assumed to be the rack id divided by 0x1000, `min_grain_samples` 64 (the system minimum) and `state_flags` 1; `uid` comes from a per-process rack counter, `render_count` is the owning system's render count and `active_voice_count` counts voices that are not empty (paused and stopped voices included). The last process ratio and tick and `active_channel_work_count` are left zero
|
||||
- [sceNgs2VoiceQueryInfo](../../core/libs/prx/libSceNgs2.native/src/Voice.cpp) (libSceNgs2.native) - only info id 0x4001 is known: PPSA22520 passes an 8-byte buffer and branches on the first word being 1 or 2, so it is assumed to be the voice channel count (0 before the voice is set up); the second word is zeroed and other ids throw
|
||||
|
|
|
|||
|
|
@ -106,6 +106,10 @@ Windows PowerShell:
|
|||
|
||||
Games that open the console's system font sets (`sceFontOpenFontSet`) need font files in an `anyps5-fonts/` directory beside the output executable; set `ANYPS5_SYSTEM_FONTS` to use another directory. Files dumped from the console are used under their own names (`SST-Roman.otf`, `SST-Bold.otf`, `SSTJpPro-Regular.otf`, ...). Without them, these openly licensed substitutes are used when present: `NotoSans-{Light,Regular,Medium,Bold}.ttf` and `NotoSans-{LightItalic,Italic,MediumItalic,BoldItalic}.ttf` (Latin and Vietnamese), `NotoSansMono-{Light,Regular,Medium,Bold}.ttf` (typewriter), `NotoSansThai-{Light,Regular,Medium,Bold}.ttf` (Thai) and `NotoSansCJK-{Light,Regular,Medium,Bold}.ttc` (Japanese and Chinese). Without either, opening a system font set fails and the game shows no text in those fonts.
|
||||
|
||||
### Console language
|
||||
|
||||
Games ask for the console's language with `sceSystemServiceParamGetInt` and show their text in it when they have it. It is English (United States) unless `ANYPS5_LANGUAGE` is set to another console language number: 0 Japanese, 1 English (United States), 2 French, 3 Spanish, 4 German, 5 Italian, 6 Dutch, 7 Portuguese (Portugal), 8 Russian, 9 Korean, 10 Chinese (Traditional), 11 Chinese (Simplified), 12 Finnish, 13 Swedish, 14 Danish, 15 Norwegian, 16 Polish, 17 Portuguese (Brazil), 18 English (United Kingdom), 19 Turkish, 20 Spanish (Latin America), 21 Arabic, 22 French (Canada), 23 Czech, 24 Hungarian, 25 Greek, 26 Romanian, 27 Thai, 28 Vietnamese, 29 Indonesian, 30 Ukrainian. For example, `ANYPS5_LANGUAGE=21` selects Arabic. An empty value is the same as an unset one; any other value stops the game at its first language query with an error that names the variable.
|
||||
|
||||
### GPU selection
|
||||
|
||||
The game runs on the first Vulkan 1.1 device with graphics and compute queues and swapchain presentation, preferring a discrete GPU over an integrated one. Set `ANYPS5_GPU` to a part of a device name, compared without regard to case, to run on another device; the names are printed at start-up in the `Physical device candidate` lines. When no usable device contains the text, the start fails and the error lists the device names.
|
||||
|
|
|
|||
Loading…
Reference in New Issue