From 71e5d87a07fe5d0d9ac3a9e3f0c70b16565047c1 Mon Sep 17 00:00:00 2001 From: capriots Date: Wed, 1 Jul 2026 08:42:17 +0200 Subject: [PATCH] cellSysmodule HLE implementation (#18962) Fully implements cellSysmodule and sets it to HLE by default. There is an undocumented function that decrypts the OpenPSID. There doesn't seem to be any information about this on the PS3 dev wiki or anywhere else, so I thought I mention this separately. --- rpcs3/Crypto/aes.cpp | 2 +- rpcs3/Crypto/aes.h | 2 +- rpcs3/Crypto/utils.cpp | 22 +- rpcs3/Crypto/utils.h | 20 +- rpcs3/Emu/Cell/Modules/cellSysmodule.cpp | 1592 +++++++++++++++++----- rpcs3/Emu/Cell/Modules/cellSysmodule.h | 144 ++ rpcs3/Emu/Cell/Modules/sysPrxForUser.cpp | 2 +- rpcs3/Emu/Cell/Modules/sysPrxForUser.h | 13 + rpcs3/Emu/Cell/lv2/sys_prx.cpp | 12 +- rpcs3/emucore.vcxproj | 1 + rpcs3/emucore.vcxproj.filters | 3 + rpcs3/util/bit_set.hpp | 1 + 12 files changed, 1467 insertions(+), 347 deletions(-) create mode 100644 rpcs3/Emu/Cell/Modules/cellSysmodule.h diff --git a/rpcs3/Crypto/aes.cpp b/rpcs3/Crypto/aes.cpp index 1cfc72aaab..1f49c9f343 100644 --- a/rpcs3/Crypto/aes.cpp +++ b/rpcs3/Crypto/aes.cpp @@ -958,7 +958,7 @@ void padding(const unsigned char *lastb, unsigned char *pad, size_t length) } } -void aes_cmac(aes_context *ctx, size_t length, unsigned char *input, unsigned char *output) +void aes_cmac(aes_context *ctx, size_t length, const unsigned char *input, unsigned char *output) { unsigned char X[16], Y[16], M_last[16], padded[16]; unsigned char K1[16], K2[16]; diff --git a/rpcs3/Crypto/aes.h b/rpcs3/Crypto/aes.h index 42e20e664d..fb4cb28d86 100644 --- a/rpcs3/Crypto/aes.h +++ b/rpcs3/Crypto/aes.h @@ -172,7 +172,7 @@ int aes_crypt_ctr( aes_context *ctx, const unsigned char *input, unsigned char *output ); -void aes_cmac(aes_context *ctx, size_t length, unsigned char *input, unsigned char *output); +void aes_cmac(aes_context *ctx, size_t length, const unsigned char *input, unsigned char *output); #ifdef __cplusplus } diff --git a/rpcs3/Crypto/utils.cpp b/rpcs3/Crypto/utils.cpp index 7913221380..c23893c906 100644 --- a/rpcs3/Crypto/utils.cpp +++ b/rpcs3/Crypto/utils.cpp @@ -77,7 +77,7 @@ void hex_to_bytes(unsigned char* data, std::string_view hex_str, usz str_length, } // Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC). -void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, usz len) +void aescbc128_decrypt(const unsigned char *key, unsigned char *iv, const unsigned char *in, unsigned char *out, usz len) { aes_context ctx; aes_setkey_dec(&ctx, key, 128); @@ -87,7 +87,7 @@ void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, memset(iv, 0, 0x10); } -void aescbc128_encrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, usz len) +void aescbc128_encrypt(const unsigned char *key, unsigned char *iv, const unsigned char *in, unsigned char *out, usz len) { aes_context ctx; aes_setkey_enc(&ctx, key, 128); @@ -97,14 +97,14 @@ void aescbc128_encrypt(unsigned char *key, unsigned char *iv, unsigned char *in, memset(iv, 0, 0x10); } -void aesecb128_encrypt(unsigned char *key, unsigned char *in, unsigned char *out) +void aesecb128_encrypt(const unsigned char *key, const unsigned char *in, unsigned char *out) { aes_context ctx; aes_setkey_enc(&ctx, key, 128); aes_crypt_ecb(&ctx, AES_ENCRYPT, in, out); } -bool hmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, usz in_len, unsigned char *hash, usz hash_len) +bool hmac_hash_compare(const unsigned char *key, int key_len, const unsigned char *in, usz in_len, const unsigned char *hash, usz hash_len) { const std::unique_ptr out(new u8[key_len]); @@ -113,12 +113,12 @@ bool hmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, usz i return std::memcmp(out.get(), hash, hash_len) == 0; } -void hmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, usz in_len, unsigned char *hash) +void hmac_hash_forge(const unsigned char *key, int key_len, const unsigned char *in, usz in_len, unsigned char *hash) { sha1_hmac(key, key_len, in, in_len, hash); } -bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, usz in_len, unsigned char *hash, usz hash_len) +bool cmac_hash_compare(const unsigned char *key, int key_len, const unsigned char *in, usz in_len, const unsigned char *hash, usz hash_len) { const std::unique_ptr out(new u8[key_len]); @@ -129,7 +129,7 @@ bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, usz i return std::memcmp(out.get(), hash, hash_len) == 0; } -void cmac_hash_forge(unsigned char *key, int /*key_len*/, unsigned char *in, usz in_len, unsigned char *hash) +void cmac_hash_forge(const unsigned char *key, int /*key_len*/, const unsigned char *in, usz in_len, unsigned char *hash) { aes_context ctx; aes_setkey_enc(&ctx, key, 128); @@ -214,7 +214,7 @@ std::array vtrm_portability_laid_paid() return sc_combine_laid_paid(0x0000000000000000L, 0x0000000000000000L); } -int sc_decrypt(const u8* sc_key, const std::array& laid_paid, u8* iv, u8* input, u8* output) +int sc_decrypt(const u8* sc_key, const std::array& laid_paid, u8* iv, const u8* input, u8* output) { aes_context ctx; u8 key[PASSPHRASE_KEY_LEN]; @@ -223,12 +223,12 @@ int sc_decrypt(const u8* sc_key, const std::array& laid_ return aes_crypt_cbc(&ctx, AES_DECRYPT, PASSPHRASE_OUT_LEN, iv, input, output); } -int vtrm_decrypt(int type, u8* iv, u8* input, u8* output) +int vtrm_decrypt(int type, u8* iv, const u8* input, u8* output) { return sc_decrypt(SC_ISO_SERIES_KEY_2, vtrm_get_laid_paid_from_type(type), iv, input, output); } -int vtrm_decrypt_master(s64 laid, s64 paid, u8* iv, u8* input, u8* output) +int vtrm_decrypt_master(s64 laid, s64 paid, u8* iv, const u8* input, u8* output) { return sc_decrypt(SC_ISO_SERIES_INTERNAL_KEY_3, sc_combine_laid_paid(laid, paid), iv, input, output); } @@ -247,7 +247,7 @@ const u8* vtrm_portability_type_mapper(int type) } } -int vtrm_decrypt_with_portability(int type, u8* iv, u8* input, u8* output) +int vtrm_decrypt_with_portability(int type, u8* iv, const u8* input, u8* output) { return sc_decrypt(vtrm_portability_type_mapper(type), vtrm_portability_laid_paid(), iv, input, output); } diff --git a/rpcs3/Crypto/utils.h b/rpcs3/Crypto/utils.h index 780226d3b9..b8fbd32d8a 100644 --- a/rpcs3/Crypto/utils.h +++ b/rpcs3/Crypto/utils.h @@ -22,17 +22,17 @@ void bytes_to_hex(std::string& hex_str, const unsigned char* data, usz data_leng void hex_to_bytes(unsigned char* data, std::string_view hex_str, usz str_length, std::string* error = nullptr); // Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC). -void aescbc128_decrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, usz len); -void aescbc128_encrypt(unsigned char *key, unsigned char *iv, unsigned char *in, unsigned char *out, usz len); -void aesecb128_encrypt(unsigned char *key, unsigned char *in, unsigned char *out); -bool hmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, usz in_len, unsigned char *hash, usz hash_len); -void hmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, usz in_len, unsigned char *hash); -bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, usz in_len, unsigned char *hash, usz hash_len); -void cmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, usz in_len, unsigned char *hash); +void aescbc128_decrypt(const unsigned char *key, unsigned char *iv, const unsigned char *in, unsigned char *out, usz len); +void aescbc128_encrypt(const unsigned char *key, unsigned char *iv, const unsigned char *in, unsigned char *out, usz len); +void aesecb128_encrypt(const unsigned char *key, const unsigned char *in, unsigned char *out); +bool hmac_hash_compare(const unsigned char *key, int key_len, const unsigned char *in, usz in_len, const unsigned char *hash, usz hash_len); +void hmac_hash_forge(const unsigned char *key, int key_len, const unsigned char *in, usz in_len, unsigned char *hash); +bool cmac_hash_compare(const unsigned char *key, int key_len, const unsigned char *in, usz in_len, const unsigned char *hash, usz hash_len); +void cmac_hash_forge(const unsigned char *key, int key_len, const unsigned char *in, usz in_len, const unsigned char *hash); void mbedtls_zeroize(void *v, size_t n); // SC passphrase crypto -int vtrm_decrypt(int type, u8* iv, u8* input, u8* output); -int vtrm_decrypt_master(s64 laid, s64 paid, u8* iv, u8* input, u8* output); -int vtrm_decrypt_with_portability(int type, u8* iv, u8* input, u8* output); +int vtrm_decrypt(int type, u8* iv, const u8* input, u8* output); +int vtrm_decrypt_master(s64 laid, s64 paid, u8* iv, const u8* input, u8* output); +int vtrm_decrypt_with_portability(int type, u8* iv, const u8* input, u8* output); diff --git a/rpcs3/Emu/Cell/Modules/cellSysmodule.cpp b/rpcs3/Emu/Cell/Modules/cellSysmodule.cpp index 41eae6bb82..5fbd42c89f 100644 --- a/rpcs3/Emu/Cell/Modules/cellSysmodule.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSysmodule.cpp @@ -1,19 +1,21 @@ #include "stdafx.h" +#include "Crypto/utils.h" +#include "Emu/Cell/lv2/sys_fs.h" +#include "Emu/Cell/lv2/sys_lwmutex.h" +#include "Emu/Cell/lv2/sys_memory.h" +#include "Emu/Cell/lv2/sys_process.h" +#include "Emu/Cell/lv2/sys_prx.h" +#include "Emu/Cell/lv2/sys_ss.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/savestate_utils.hpp" +#include "sysPrxForUser.h" +#include "util/bit_set.hpp" + +#include "cellSysmodule.h" +#include LOG_CHANNEL(cellSysmodule); -constexpr auto CELL_SYSMODULE_LOADED = CELL_OK; - -enum CellSysmoduleError : u32 -{ - CELL_SYSMODULE_ERROR_DUPLICATED = 0x80012001, - CELL_SYSMODULE_ERROR_UNKNOWN = 0x80012002, - CELL_SYSMODULE_ERROR_UNLOADED = 0x80012003, - CELL_SYSMODULE_ERROR_INVALID_MEMCONTAINER = 0x80012004, - CELL_SYSMODULE_ERROR_FATAL = 0x800120ff, -}; - template<> void fmt_class_string::format(std::string& out, u64 arg) { @@ -32,406 +34,1348 @@ void fmt_class_string::format(std::string& out, u64 arg) }); } -static const char* get_module_name(u16 id) +namespace { - switch (id) - { - case 0x0000: return "sys_net"; - case 0x0001: return "cellHttp"; - case 0x0002: return "cellHttpUtil"; - case 0x0003: return "cellSsl"; - case 0x0004: return "cellHttps"; - case 0x0005: return "libvdec"; - case 0x0006: return "cellAdec"; - case 0x0007: return "cellDmux"; - case 0x0008: return "cellVpost"; - case 0x0009: return "cellRtc"; - case 0x000a: return "cellSpurs"; - case 0x000b: return "cellOvis"; - case 0x000c: return "cellSheap"; - case 0x000d: return "cellSync"; - case 0x000e: return "sys_fs"; - case 0x000f: return "cellJpgDec"; - case 0x0010: return "cellGcmSys"; - case 0x0011: return "cellAudio"; - case 0x0012: return "cellPamf"; - case 0x0013: return "cellAtrac"; - case 0x0014: return "cellNetCtl"; - case 0x0015: return "cellSysutil"; - case 0x0016: return "sceNp"; - case 0x0017: return "sys_io"; - case 0x0018: return "cellPngDec"; - case 0x0019: return "cellFont"; - case 0x001a: return "cellFontFT"; - case 0x001b: return "cell_FreeType2"; - case 0x001c: return "cellUsbd"; - case 0x001d: return "cellSail"; - case 0x001e: return "cellL10n"; - case 0x001f: return "cellResc"; - case 0x0020: return "cellDaisy"; - case 0x0021: return "cellKey2char"; - case 0x0022: return "cellMic"; - case 0x0023: return "cellCamera"; - case 0x0024: return "cellVdecMpeg2"; - case 0x0025: return "cellVdecAvc"; - case 0x0026: return "cellAdecLpcm"; - case 0x0027: return "cellAdecAc3"; - case 0x0028: return "cellAdecAtx"; - case 0x0029: return "cellAdecAt3"; - case 0x002a: return "cellDmuxPamf"; - case 0x002b: return nullptr; - case 0x002c: return nullptr; - case 0x002d: return nullptr; - case 0x002e: return "sys_lv2dbg"; - case 0x002f: return "cellSysutilAvcExt"; - case 0x0030: return "cellUsbPspcm"; - case 0x0031: return "cellSysutilAvconfExt"; - case 0x0032: return "cellUserInfo"; - case 0x0033: return "cellSaveData"; - case 0x0034: return "cellSubDisplay"; - case 0x0035: return "cellRec"; - case 0x0036: return "cellVideoExportUtility"; - case 0x0037: return "cellGameExec"; - case 0x0038: return "sceNp2"; - case 0x0039: return "cellSysutilAp"; - case 0x003a: return "sceNpClans"; - case 0x003b: return "cellOskExtUtility"; - case 0x003c: return "cellVdecDivx"; - case 0x003d: return "cellJpgEnc"; - case 0x003e: return "cellGame"; - case 0x003f: return "cellBGDLUtility"; - case 0x0040: return "cell_FreeType2"; - case 0x0041: return "cellVideoUpload"; - case 0x0042: return "cellSysconfExtUtility"; - case 0x0043: return "cellFiber"; - case 0x0044: return "sceNpCommerce2"; - case 0x0045: return "sceNpTus"; - case 0x0046: return "cellVoice"; - case 0x0047: return "cellAdecCelp8"; - case 0x0048: return "cellCelp8Enc"; - case 0x0049: return "cellSysutilMisc"; - case 0x004a: return "cellMusicUtility"; - // TODO: Check if those libad are correctly matched. - // They belong to those IDs but actual order is unknown. - case 0x004b: return "libad_core"; - case 0x004c: return "libad_async"; - case 0x004d: return "libad_billboard_util"; - case 0x004e: return "cellScreenShotUtility"; - case 0x004f: return "cellMusicDecodeUtility"; - case 0x0050: return "cellSpursJq"; - case 0x0052: return "cellPngEnc"; - case 0x0053: return "cellMusicDecodeUtility"; - case 0x0054: return "libmedi"; - case 0x0055: return "cellSync2"; - case 0x0056: return "sceNpUtil"; - case 0x0057: return "cellRudp"; - case 0x0059: return "sceNpSns"; - case 0x005a: return "libgem"; - case 0x005c: return "cellCrossController"; - case 0xf00a: return "cellCelpEnc"; - case 0xf010: return "cellGifDec"; - case 0xf019: return "cellAdecCelp"; - case 0xf01b: return "cellAdecM2bc"; - case 0xf01d: return "cellAdecM4aac"; - case 0xf01e: return "cellAdecMp3"; - case 0xf023: return "cellImeJpUtility"; - case 0xf028: return "cellMusicUtility"; - case 0xf029: return "cellPhotoUtility"; - case 0xf02a: return "cellPrintUtility"; - case 0xf02b: return "cellPhotoImportUtil"; - case 0xf02c: return "cellMusicExportUtility"; - case 0xf02e: return "cellPhotoDecodeUtil"; - case 0xf02f: return "cellSearchUtility"; - case 0xf030: return "cellSysutilAvc2"; - case 0xf034: return "cellSailRec"; - case 0xf035: return "sceNpTrophy"; - case 0xf044: return "cellSysutilNpEula"; - case 0xf053: return "cellAdecAt3multi"; - case 0xf054: return "cellAtracMulti"; - } + constexpr u16 INTERNAL_MODULE_ID_BASE = 0xf000; + constexpr u16 INTERNAL_MODULE_ID_MASK = 0x0fff; + constexpr u16 UNK_MODULE_ID_BASE = 0xff00; + constexpr u8 UNK_MODULE_ID_MASK = 0x00ff; - return nullptr; + constexpr std::array DEPENDENCIES_NET { CELL_SYSMODULE_NETCTL }; + constexpr std::array DEPENDENCIES_HTTP { CELL_SYSMODULE_NET, CELL_SYSMODULE_RTC }; + constexpr std::array DEPENDENCIES_HTTPUTIL{ CELL_SYSMODULE_HTTP }; + constexpr std::array DEPENDENCIES_HTTPS { CELL_SYSMODULE_SSL, CELL_SYSMODULE_HTTP }; + constexpr std::array DEPENDENCIES_SSL { CELL_SYSMODULE_NET, CELL_SYSMODULE_RTC, CELL_SYSMODULE_FS }; + constexpr std::array DEPENDENCIES_NP2 { CELL_SYSMODULE_SYSUTIL_NP }; + constexpr std::array DEPENDENCIES_CLANS { CELL_SYSMODULE_SYSUTIL_NP, CELL_SYSMODULE_HTTPS }; + constexpr std::array DEPENDENCIES_FS { CELL_SYSMODULE_FS }; + constexpr std::array DEPENDENCIES_AT3P { CELL_SYSMODULE_ADEC_AT3, CELL_SYSMODULE_ADEC_ATX }; + constexpr std::array DEPENDENCIES_AT3M { 0xf053 }; + constexpr std::array DEPENDENCIES_SPURS { CELL_SYSMODULE_SPURS }; + constexpr std::array DEPENDENCIES_VDEC_AL { CELL_SYSMODULE_VDEC_AL }; + constexpr std::array DEPENDENCIES_ADEC_AL { CELL_SYSMODULE_ADEC_AL }; + constexpr std::array DEPENDENCIES_ADEC2 { 0xf03f }; + constexpr std::array DEPENDENCIES_DMUX_AL { CELL_SYSMODULE_DMUX_AL }; + constexpr std::array DEPENDENCIES_VDEC { CELL_SYSMODULE_VDEC_MPEG2, CELL_SYSMODULE_VDEC_AVC, CELL_SYSMODULE_VDEC_AL }; + constexpr std::array DEPENDENCIES_ADEC { CELL_SYSMODULE_ADEC_LPCM, CELL_SYSMODULE_ADEC_AC3, CELL_SYSMODULE_ADEC_AT3, CELL_SYSMODULE_ADEC_ATX, CELL_SYSMODULE_ADEC_AL }; + constexpr std::array DEPENDENCIES_DMUX { CELL_SYSMODULE_DMUX_PAMF, CELL_SYSMODULE_DMUX_AL }; + constexpr std::array DEPENDENCIES_PSPCM { CELL_SYSMODULE_USBD }; + constexpr std::array DEPENDENCIES_COMM { CELL_SYSMODULE_SYSUTIL_NP2, CELL_SYSMODULE_HTTPS }; + constexpr std::array DEPENDENCIES_NP_TUS { CELL_SYSMODULE_SYSUTIL_NP2 }; + constexpr std::array DEPENDENCIES_AD_CORE { CELL_SYSMODULE_FS, CELL_SYSMODULE_NET, CELL_SYSMODULE_SYSUTIL_NP }; + constexpr std::array DEPENDENCIES_AD_ASYNC{ 0x4b }; + constexpr std::array DEPENDENCIES_SPURS_JQ{ CELL_SYSMODULE_FIBER }; + constexpr std::array DEPENDENCIES_MEDI { CELL_SYSMODULE_NET, CELL_SYSMODULE_SYSUTIL_NP }; + + struct module_info + { + std::string_view path; + std::span dependencies; + }; + + constexpr std::array MODULE_INFOS + { + // 93 external modules + module_info + { .path = "external/libnet.sprx", .dependencies = DEPENDENCIES_NET }, // CELL_SYSUTIL_NET + { .path = "external/libhttp.sprx", .dependencies = DEPENDENCIES_HTTP }, // CELL_SYSMODULE_HTTP + { .path = {}, .dependencies = DEPENDENCIES_HTTPUTIL }, // CELL_SYSMODULE_HTTP_UTIL + { .path = "external/libssl.sprx", .dependencies = DEPENDENCIES_SSL }, // CELL_SYSMODULE_SSL + { .path = {}, .dependencies = DEPENDENCIES_HTTPS }, // CELL_SYSMODULE_HTTPS + { .path = {}, .dependencies = DEPENDENCIES_VDEC }, // CELL_SYSMODULE_VDEC + { .path = {}, .dependencies = DEPENDENCIES_ADEC }, // CELL_SYSMODULE_ADEC + { .path = {}, .dependencies = DEPENDENCIES_DMUX }, // CELL_SYSMODULE_DMUX + { .path = "external/libvpost.sprx", .dependencies = {} }, // CELL_SYSMODULE_VPOST + { .path = "external/librtc.sprx", .dependencies = {} }, // CELL_SYSMODULE_RTC + { .path = "external/libsre.sprx", .dependencies = {} }, // CELL_SYSMODULE_SPURS + { .path = {}, .dependencies = DEPENDENCIES_SPURS }, // CELL_SYSMODULE_OVIS + { .path = {}, .dependencies = DEPENDENCIES_SPURS }, // CELL_SYSMODULE_SHEAP + { .path = {}, .dependencies = DEPENDENCIES_SPURS }, // CELL_SYSMODULE_SYNC + { .path = "external/libfs.sprx", .dependencies = {} }, // CELL_SYSMODULE_FS + { .path = "external/libjpgdec.sprx", .dependencies = DEPENDENCIES_FS }, // CELL_SYSMODULE_JPGDEC + { .path = "external/libgcm_sys.sprx", .dependencies = {} }, // CELL_SYSMODULE_GCM_SYS + { .path = "external/libaudio.sprx", .dependencies = {} }, // CELL_SYSMODULE_AUDIO + { .path = "external/libpamf.sprx", .dependencies = {} }, // CELL_SYSMODULE_PAMF + { .path = "external/libatrac3plus.sprx", .dependencies = DEPENDENCIES_AT3P }, // CELL_SYSMODULE_ATRAC3PLUS + { .path = "external/libnetctl.sprx", .dependencies = {} }, // CELL_SYSMODULE_NETCTL + { .path = "external/libsysutil.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL + { .path = "external/libsysutil_np.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_NP + { .path = "external/libio.sprx", .dependencies = {} }, // CELL_SYSMODULE_IO + { .path = "external/libpngdec.sprx", .dependencies = DEPENDENCIES_FS }, // CELL_SYSMODULE_PNGDEC + { .path = "external/libfont.sprx", .dependencies = DEPENDENCIES_FS }, // CELL_SYSMODULE_FONT + { .path = "external/libfontFT.sprx", .dependencies = {} }, // CELL_SYSMODULE_FONTFT + { .path = "external/libfreetype.sprx", .dependencies = DEPENDENCIES_FS }, // CELL_SYSMODULE_FREETYPE + { .path = "external/libusbd.sprx", .dependencies = {} }, // CELL_SYSMODULE_USBD + { .path = "external/libsail.sprx", .dependencies = {} }, // CELL_SYSMODULE_SAIL + { .path = "external/libl10n.sprx", .dependencies = {} }, // CELL_SYSMODULE_L10N + { .path = "external/libresc.sprx", .dependencies = {} }, // CELL_SYSMODULE_RESC + { .path = {}, .dependencies = DEPENDENCIES_SPURS }, // CELL_SYSMODULE_DAISY + { .path = "external/libkey2char.sprx", .dependencies = {} }, // CELL_SYSMODULE_KEY2CHAR + { .path = "external/libmic.sprx", .dependencies = {} }, // CELL_SYSMODULE_MIC + { .path = "external/libcamera.sprx", .dependencies = {} }, // CELL_SYSMODULE_CAMERA + { .path = "external/libsmvd2.sprx", .dependencies = DEPENDENCIES_VDEC_AL }, // CELL_SYSMODULE_VDEC_MPEG2 + { .path = "external/libavcdec.sprx", .dependencies = DEPENDENCIES_VDEC_AL }, // CELL_SYSMODULE_VDEC_AVC + { .path = {}, .dependencies = DEPENDENCIES_ADEC_AL }, // CELL_SYSMODULE_ADEC_LPCM + { .path = "external/libac3dec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // CELL_SYSMODULE_ADEC_AC3 + { .path = "external/libatxdec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // CELL_SYSMODULE_ADEC_ATX + { .path = "external/libat3dec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // CELL_SYSMODULE_ADEC_AT3 + { .path = "external/libdmuxpamf.sprx", .dependencies = DEPENDENCIES_DMUX_AL }, // CELL_SYSMODULE_DMUX_PAMF + { .path = "external/libvdec.sprx", .dependencies = {} }, // CELL_SYSMODULE_VDEC_AL + { .path = "external/libadec.sprx", .dependencies = {} }, // CELL_SYSMODULE_ADEC_AL + { .path = "external/libdmux.sprx", .dependencies = {} }, // CELL_SYSMODULE_DMUX_AL + { .path = "external/liblv2dbg_for_dex.sprx", .dependencies = {} }, // CELL_SYSMODULE_LV2DBG + { .path = "external/libsysutil_avc_ext.sprx", .dependencies = {} }, // 0x2f + { .path = "external/libusbpspcm.sprx", .dependencies = DEPENDENCIES_PSPCM }, // CELL_SYSMODULE_USBPSPCM + { .path = "external/libsysutil_avconf_ext.sprx", .dependencies = {} }, // CELL_SYSMODULE_AVCONF_EXT + { .path = "external/libsysutil_userinfo.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_USERINFO + { .path = "external/libsysutil_savedata.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_SAVEDATA + { .path = "external/libsysutil_subdisplay.sprx", .dependencies = {} }, // CELL_SYSMODULE_SUBDISPLAY + { .path = "external/libsysutil_rec.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_REC + { .path = "external/libsysutil_video_export.sprx", .dependencies = {} }, // CELL_SYSMODULE_VIDEO_EXPORT + { .path = "external/libsysutil_game_exec.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_GAME_EXEC + { .path = "external/libsysutil_np2.sprx", .dependencies = DEPENDENCIES_NP2 }, // CELL_SYSMODULE_SYSUTIL_NP2 + { .path = "external/libsysutil_ap.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_AP + { .path = "external/libsysutil_np_clans.sprx", .dependencies = DEPENDENCIES_CLANS }, // CELL_SYSMODULE_SYSUTIL_NP_CLANS + { .path = "external/libsysutil_oskdialog_ext.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_OSK_EXT + { .path = "external/libdivxdec.sprx", .dependencies = DEPENDENCIES_VDEC_AL }, // CELL_SYSMODULE_VDEC_DIVX + { .path = "external/libjpgenc.sprx", .dependencies = DEPENDENCIES_FS }, // CELL_SYSMODULE_JPGENC + { .path = "external/libsysutil_game.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_GAME + { .path = "external/libsysutil_bgdl.sprx", .dependencies = {} }, // CELL_SYSMODULE_BGDL + { .path = "external/libfreetypeTT.sprx", .dependencies = DEPENDENCIES_FS }, // CELL_SYSMODULE_FREETYPE_TT + { .path = "external/libsysutil_video_upload.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_VIDEO_UPLOAD + { .path = "external/libsysutil_sysconf_ext.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_SYSCONF_EXT + { .path = "external/libfiber.sprx", .dependencies = {} }, // CELL_SYSMODULE_FIBER + { .path = "external/libsysutil_np_commerce2.sprx", .dependencies = DEPENDENCIES_COMM }, // CELL_SYSMODULE_SYSUTIL_NP_COMMERCE2 + { .path = "external/libsysutil_np_tus.sprx", .dependencies = DEPENDENCIES_NP_TUS }, // CELL_SYSMODULE_SYSUTIL_NP_TUS + { .path = "external/libvoice.sprx", .dependencies = {} }, // CELL_SYSMODULE_VOICE + { .path = "external/libcelp8dec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // CELL_SYSMODULE_ADEC_CELP8 + { .path = "external/libcelp8enc.sprx", .dependencies = {} }, // CELL_SYSMODULE_CELP8ENC + { .path = "external/libsysutil_misc.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_LICENSEAREA + { .path = "external/libsysutil_music.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_MUSIC2 + { .path = "external/libad_core.sprx", .dependencies = DEPENDENCIES_AD_CORE }, // 0x4b + { .path = "external/libad_async.sprx", .dependencies = DEPENDENCIES_AD_ASYNC }, // 0x4c + { .path = "external/libad_billboard_util.sprx", .dependencies = DEPENDENCIES_AD_CORE }, // 0x4d + { .path = "external/libsysutil_screenshot.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_SCREENSHOT + { .path = "external/libsysutil_music_decode.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_MUSIC_DECODE + { .path = "external/libspurs_jq.sprx", .dependencies = DEPENDENCIES_SPURS_JQ }, // CELL_SYSMODULE_SPURS_JQ + { .path = "external/libsysutil_authdialog.sprx", .dependencies = {} }, // 0x51 + { .path = "external/libpngenc.sprx", .dependencies = DEPENDENCIES_FS }, // CELL_SYSMODULE_PNGENC + { .path = "external/libsysutil_music_decode.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_MUSIC_DECODE2 + { .path = "external/libmedi.sprx", .dependencies = DEPENDENCIES_MEDI }, // 0x54 + { .path = "external/libsync2.sprx", .dependencies = DEPENDENCIES_SPURS_JQ }, // CELL_SYSMODULE_SYNC2 + { .path = "external/libsysutil_np_util.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_NP_UTIL + { .path = "external/librudp.sprx", .dependencies = {} }, // CELL_SYSMODULE_RUDP + { .path = "external/libsysutil_syschat.sprx", .dependencies = {} }, // 0x58 + { .path = "external/libsysutil_np_sns.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_NP_SNS + { .path = "external/libgem.sprx", .dependencies = {} }, // CELL_SYSMODULE_GEM + { .path = "external/libsysutil_photo_network_sharing.sprx", .dependencies = {} }, // 0x5b + { .path = "external/libsysutil_cross_controller.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_CROSS_CONTROLLER + + // 87 internal modules + { .path = "internal/libat3enc.sprx", .dependencies = {} }, // 0xf000 + { .path = "internal/libatxenc.sprx", .dependencies = {} }, // 0xf001 + { .path = "external/libvdec.sprx", .dependencies = {} }, // 0xf002 + { .path = "internal/libvpost.sprx", .dependencies = {} }, // 0xf003 + { .path = "internal/libmp3enc.sprx", .dependencies = DEPENDENCIES_FS }, // 0xf004 + { .path = "external/libaacenc.sprx", .dependencies = {} }, // 0xf005 + { .path = "external/libadec_internal.sprx", .dependencies = {} }, // 0xf006 + { .path = "internal/libapostsrc.sprx", .dependencies = {} }, // 0xf007 + { .path = "internal/libaudio_internal.sprx", .dependencies = {} }, // 0xf008 + { .path = "external/libavchatjpgdec.sprx", .dependencies = {} }, // 0xf009 + { .path = "external/libcelpenc.sprx", .dependencies = {} }, // CELL_SYSMODULE_CELPENC + { .path = "internal/libddlenc.sprx", .dependencies = {} }, // 0xf00b + { .path = "external/libdmux.sprx", .dependencies = {} }, // 0xf00c + { .path = "internal/libexif.sprx", .dependencies = DEPENDENCIES_FS }, // 0xf00d + { .path = "internal/libft2d.sprx", .dependencies = {} }, // 0xf00e + { .path = "internal/libgcm_osd.sprx", .dependencies = {} }, // 0xf00f + { .path = "external/libgifdec.sprx", .dependencies = DEPENDENCIES_FS }, // CELL_SYSMODULE_GIFDEC + { .path = "external/libjpgdec.sprx", .dependencies = DEPENDENCIES_FS }, // 0xf011 + { .path = "external/libjpgenc.sprx", .dependencies = DEPENDENCIES_FS }, // 0xf012 + { .path = "external/libm4venc.sprx", .dependencies = {} }, // 0xf013 + { .path = "external/libpamf.sprx", .dependencies = {} }, // 0xf014 + { .path = "external/libpngdec.sprx", .dependencies = DEPENDENCIES_FS }, // 0xf015 + { .path = "external/libpngenc.sprx", .dependencies = DEPENDENCIES_FS }, // 0xf016 + { .path = "internal/libps2savedata.sprx", .dependencies = {} }, // 0xf017 + { .path = "internal/libtiffdec.sprx", .dependencies = DEPENDENCIES_FS }, // 0xf018 + { .path = "external/libcelpdec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // CELL_SYSMODULE_ADEC_CELP + { .path = "internal/libdtsdec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // 0xf01a + { .path = "external/libm2bcdec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // CELL_SYSMODULE_ADEC_M2BC + { .path = "internal/libm2aacdec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // 0xf01c + { .path = "external/libm4aacdec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // CELL_SYSMODULE_ADEC_M4AAC + { .path = "external/libmp3dec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // CELL_SYSMODULE_ADEC_MP3 + { .path = "internal/libtrhddec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // 0xf01f + { .path = "external/libsvc1d.sprx", .dependencies = DEPENDENCIES_VDEC_AL }, // 0xf020 + { .path = "external/libsmvd4.sprx", .dependencies = DEPENDENCIES_VDEC_AL }, // 0xf021 + { .path = "external/libsysutil_remoteplay.sprx", .dependencies = {} }, // 0xf022 + { .path = "external/libsysutil_imejp.sprx", .dependencies = {} }, // CELL_SYSMODULE_IMEJP + { .path = "external/libwmadec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // 0xf024 + { .path = "internal/libasfparser.sprx", .dependencies = {} }, // 0xf025 + { .path = "external/libddpdec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // 0xf026 + { .path = "internal/libdtslbrdec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // 0xf027 + { .path = "external/libsysutil_music.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_MUSIC + { .path = "external/libsysutil_photo_export.sprx", .dependencies = {} }, // CELL_SYSMODULE_PHOTO_EXPORT + { .path = "external/libsysutil_print.sprx", .dependencies = {} }, // CELL_SYSMODULE_PRINT + { .path = "external/libsysutil_photo_import.sprx", .dependencies = {} }, // CELL_SYSMODULE_PHOTO_IMPORT + { .path = "external/libsysutil_music_export.sprx", .dependencies = {} }, // CELL_SYSMODULE_MUSIC_EXPORT + { .path = "external/libavcenc_small.sprx", .dependencies = {} }, // 0xf02d + { .path = "external/libsysutil_photo_decode.sprx", .dependencies = {} }, // CELL_SYSMODULE_PHOTO_DECODE + { .path = "external/libsysutil_search.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_SEARCH + { .path = "external/libsysutil_avc2.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_AVCHAT2 + { .path = "external/libmp4.sprx", .dependencies = {} }, // 0xf031 + { .path = "external/libsysutil_rtcalarm.sprx", .dependencies = {} }, // 0xf032 + { .path = "external/libavcenc_small.sprx", .dependencies = {} }, // 0xf033 + { .path = "external/libsail_rec.sprx", .dependencies = {} }, // CELL_SYSMODULE_SAIL_REC + { .path = "external/libsysutil_np_trophy.sprx", .dependencies = {} }, // CELL_SYSMODULE_SYSUTIL_NP_TROPHY + { .path = "external/libsjvtd.sprx", .dependencies = DEPENDENCIES_VDEC_AL }, // 0xf036 + { .path = "internal/libdtshddec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // 0xf037 + { .path = "internal/libmp3sdec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // 0xf038 + { .path = "external/libsail_avi.sprx", .dependencies = {} }, // 0xf039 + { .path = "external/libavcenc.sprx", .dependencies = {} }, // 0xf03a + { .path = "external/libapostsrc_mini.sprx", .dependencies = {} }, // 0xf03b + { .path = "external/libvoice_internal.sprx", .dependencies = {} }, // 0xf03c + { .path = "external/libmpl1dec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // 0xf03d + { .path = "external/libasfparser2_astd.sprx", .dependencies = {} }, // 0xf03e + { .path = "external/libadec2.sprx", .dependencies = {} }, // 0xf03f + { .path = "external/libac3dec2.sprx", .dependencies = DEPENDENCIES_ADEC2 }, // 0xf040 + { .path = "external/libatxdec2.sprx", .dependencies = DEPENDENCIES_ADEC2 }, // 0xf041 + { .path = "internal/libdivx311dec.sprx", .dependencies = DEPENDENCIES_VDEC_AL }, // 0xf042 + { .path = "external/libvpost2.sprx", .dependencies = {} }, // 0xf043 + { .path = "external/libsysutil_np_eula.sprx", .dependencies = {} }, // 0xf044 + { .path = "external/libsysutil_storagedata.sprx", .dependencies = {} }, // 0xf045 + { .path = "external/libm4hdenc.sprx", .dependencies = {} }, // 0xf046 + { .path = "external/libsysutil_savedata_psp.sprx", .dependencies = {} }, // 0xf047 + { .path = "external/libsysutil_video_player.sprx", .dependencies = {} }, // 0xf048 + { .path = "external/libmvcdec.sprx", .dependencies = DEPENDENCIES_VDEC_AL }, // 0xf049 + { .path = "external/libaacenc_spurs.sprx", .dependencies = {} }, // 0xf04a + { .path = "internal/libdtshdcoredec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // 0xf04b + { .path = "internal/libpidvd.sprx", .dependencies = {} }, // 0xf04c + { .path = "external/libsysutil_dtcp_ip.sprx", .dependencies = {} }, // 0xf04d + { .path = "external/libsysutil_syschat.sprx", .dependencies = {} }, // 0xf04e + { .path = "external/libsysutil_np_installer.sprx", .dependencies = {} }, // 0xf04f + { .path = "external/libbemp2sys.sprx", .dependencies = {} }, // 0xf050 + { .path = "external/libbeisobmf.sprx", .dependencies = {} }, // 0xf051 + { .path = "external/libsysutil_photo_export2.sprx", .dependencies = {} }, // 0xf052 + { .path = "external/libat3multidec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // 0xf053 + { .path = "external/libatrac3multi.sprx", .dependencies = DEPENDENCIES_AT3M }, // CELL_SYSMODULE_LIBATRAC3MULTI + { .path = "external/libsysutil_dec_psnvideo.sprx", .dependencies = {} }, // 0xf055 + { .path = "external/libdtslbrdec.sprx", .dependencies = DEPENDENCIES_ADEC_AL }, // 0xf056 + + // Special modules + { .path = "external/liblv2dbg_for_dex.sprx", .dependencies = {} }, + { .path = "external/liblv2dbg_for_cex.sprx", .dependencies = {} }, + + { .path = "external/libgcm_sys_deh.sprx", .dependencies = {} }, + { .path = "external/libgcm_sys.sprx", .dependencies = {} }, + + { .path = "external/libfs.sprx", .dependencies = {} }, + { .path = "external/libfs_155.sprx", .dependencies = {} }, + + { .path = "external/libprof.sprx", .dependencies = {} }, + { .path = "external/liblv2coredump.sprx", .dependencies = {} }, + { .path = "external/libgpad.sprx", .dependencies = {} } + }; + + constexpr u8 INTERNAL_MODULES_OFFSET = 93; + constexpr u8 INTERNAL_MODULES_COUNT = 87; + constexpr u8 LV2_DBG_IDX = INTERNAL_MODULES_OFFSET + INTERNAL_MODULES_COUNT; + constexpr u8 GCM_SYS_DEH_IDX = LV2_DBG_IDX + 2; + constexpr u8 FS_IDX = LV2_DBG_IDX + 4; + constexpr u8 PROF_IDX = LV2_DBG_IDX + 6; + constexpr u8 LV2COREDUMP_IDX = LV2_DBG_IDX + 7; + constexpr u8 GPAD_IDX = LV2_DBG_IDX + 8; + + constexpr std::string_view MODULE_BASE_PATH = "/dev_flash/sys/"; + constexpr std::string_view MODULE_BASE_PATH_DEBUG = "/app_home/"; + constexpr u8 BASE_PATH_MAX_SIZE = 0x10; + constexpr u8 PATH_MAX_SIZE = 0x40; + static_assert(MODULE_BASE_PATH.size() < BASE_PATH_MAX_SIZE && MODULE_BASE_PATH_DEBUG.size() < BASE_PATH_MAX_SIZE); + static_assert(std::ranges::all_of(MODULE_INFOS, [](const auto& path) { return path.size() + BASE_PATH_MAX_SIZE < PATH_MAX_SIZE; }, &module_info::path)); + + constexpr std::array DEFAULT_MODULES + { + CELL_SYSMODULE_SYSUTIL, + CELL_SYSMODULE_GCM_SYS, + CELL_SYSMODULE_AUDIO, + CELL_SYSMODULE_IO, + CELL_SYSMODULE_SPURS, + CELL_SYSMODULE_FS, + CELL_SYSMODULE_SYSUTIL_NP_TROPHY + }; + + constexpr bit_set LOADABLE_INTERNAL_MODULES + { + "0010000" // 0xf054 + "0000000000010000" // 0xf044 + "0000000000110001" // 0xf030, 0xf034, 0xf035 + "1101111100001000" // 0xf023, 0xf028, 0xf029, 0xf02a, 0xf02b, 0xf02c, 0xf02e, 0xf02f + "0110101000000001" // 0xf010, 0xf019, 0xf01b, 0xf01d, 0xf01e + "0000010000000000" // 0xf00a + }; + + constexpr bit_set LOADABLE_INTERNAL_MODULES_EX + { + "1110111" // 0xf050, 0xf051, 0xf052, 0xf054, 0xf055, 0xf056 + "1110011111111011" // 0xf040, 0xf041, 0xf043, 0xf044, 0xf045, 0xf046, 0xf047, 0xf048, 0xf049, 0xf04a, 0xf04d, 0xf04e, 0xf04f + "1110011001111101" // 0xf030, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf039, 0xf03a, 0xf03d, 0xf03e, 0xf03f + "1101111101011111" // 0xf020, 0xf021, 0xf022, 0xf023, 0xf024, 0xf026, 0xf028, 0xf029, 0xf02a, 0xf02b, 0xf02c, 0xf02e, 0xf02f + "0110101000001001" // 0xf010, 0xf013, 0xf019, 0xf01b, 0xf01d, 0xf01e + "0000010000100000" // 0xf005, 0xf00a + }; + + constexpr std::array KEY_UNK{ 'S', 'r', 'e', 'k', 'i', 'r', 't', 'S', 'a', 'h', 'o', 'n', 'a', 'N', 'l', 'a' }; + + struct module_state + { + s32 loaded_count; + s32 prx_id; + }; + + struct sysmodule_context + { + sys_lwmutex_t mutex; + + be_t unk_addr; + + u32 mem_container_id; + b8 use_mem_container; + + module_state module_states[MODULE_INFOS.size()]; + + char module_base_path[BASE_PATH_MAX_SIZE]; + u8 module_base_path_size; + }; + + vm::gvar s_sysmodule_context; } -static const char* get_module_id(u16 id) +template +[[nodiscard]] static bool check_special_handling(ppu_thread& ppu) { - static thread_local char tls_id_name[8]; // for test + // TODO replace with proper struct + const vm::var paramsfo(0x40); + std::memset(paramsfo.get_ptr(), 0, paramsfo.get_count()); - switch (id) + const error_code ret = ppu_execute<&sys_process_get_paramsfo>(ppu, +paramsfo); + + switch (module_id) { - case 0x0000: return "CELL_SYSMODULE_NET"; - case 0x0001: return "CELL_SYSMODULE_HTTP"; - case 0x0002: return "CELL_SYSMODULE_HTTP_UTIL"; - case 0x0003: return "CELL_SYSMODULE_SSL"; - case 0x0004: return "CELL_SYSMODULE_HTTPS"; - case 0x0005: return "CELL_SYSMODULE_VDEC"; - case 0x0006: return "CELL_SYSMODULE_ADEC"; - case 0x0007: return "CELL_SYSMODULE_DMUX"; - case 0x0008: return "CELL_SYSMODULE_VPOST"; - case 0x0009: return "CELL_SYSMODULE_RTC"; - case 0x000a: return "CELL_SYSMODULE_SPURS"; - case 0x000b: return "CELL_SYSMODULE_OVIS"; - case 0x000c: return "CELL_SYSMODULE_SHEAP"; - case 0x000d: return "CELL_SYSMODULE_SYNC"; - case 0x000e: return "CELL_SYSMODULE_FS"; - case 0x000f: return "CELL_SYSMODULE_JPGDEC"; - case 0x0010: return "CELL_SYSMODULE_GCM_SYS"; - case 0x0011: return "CELL_SYSMODULE_AUDIO"; - case 0x0012: return "CELL_SYSMODULE_PAMF"; - case 0x0013: return "CELL_SYSMODULE_ATRAC3PLUS"; - case 0x0014: return "CELL_SYSMODULE_NETCTL"; - case 0x0015: return "CELL_SYSMODULE_SYSUTIL"; - case 0x0016: return "CELL_SYSMODULE_SYSUTIL_NP"; - case 0x0017: return "CELL_SYSMODULE_IO"; - case 0x0018: return "CELL_SYSMODULE_PNGDEC"; - case 0x0019: return "CELL_SYSMODULE_FONT"; - case 0x001a: return "CELL_SYSMODULE_FONTFT"; - case 0x001b: return "CELL_SYSMODULE_FREETYPE"; - case 0x001c: return "CELL_SYSMODULE_USBD"; - case 0x001d: return "CELL_SYSMODULE_SAIL"; - case 0x001e: return "CELL_SYSMODULE_L10N"; - case 0x001f: return "CELL_SYSMODULE_RESC"; - case 0x0020: return "CELL_SYSMODULE_DAISY"; - case 0x0021: return "CELL_SYSMODULE_KEY2CHAR"; - case 0x0022: return "CELL_SYSMODULE_MIC"; - case 0x0023: return "CELL_SYSMODULE_CAMERA"; - case 0x0024: return "CELL_SYSMODULE_VDEC_MPEG2"; - case 0x0025: return "CELL_SYSMODULE_VDEC_AVC"; - case 0x0026: return "CELL_SYSMODULE_ADEC_LPCM"; - case 0x0027: return "CELL_SYSMODULE_ADEC_AC3"; - case 0x0028: return "CELL_SYSMODULE_ADEC_ATX"; - case 0x0029: return "CELL_SYSMODULE_ADEC_AT3"; - case 0x002a: return "CELL_SYSMODULE_DMUX_PAMF"; - case 0x002b: return "CELL_SYSMODULE_VDEC_AL"; - case 0x002c: return "CELL_SYSMODULE_ADEC_AL"; - case 0x002d: return "CELL_SYSMODULE_DMUX_AL"; - case 0x002e: return "CELL_SYSMODULE_LV2DBG"; - case 0x002f: return "CELL_SYSMODULE_SYSUTIL_AVCHAT"; - case 0x0030: return "CELL_SYSMODULE_USBPSPCM"; - case 0x0031: return "CELL_SYSMODULE_AVCONF_EXT"; - case 0x0032: return "CELL_SYSMODULE_SYSUTIL_USERINFO"; - case 0x0033: return "CELL_SYSMODULE_SYSUTIL_SAVEDATA"; - case 0x0034: return "CELL_SYSMODULE_SUBDISPLAY"; - case 0x0035: return "CELL_SYSMODULE_SYSUTIL_REC"; - case 0x0036: return "CELL_SYSMODULE_VIDEO_EXPORT"; - case 0x0037: return "CELL_SYSMODULE_SYSUTIL_GAME_EXEC"; - case 0x0038: return "CELL_SYSMODULE_SYSUTIL_NP2"; - case 0x0039: return "CELL_SYSMODULE_SYSUTIL_AP"; - case 0x003a: return "CELL_SYSMODULE_SYSUTIL_NP_CLANS"; - case 0x003b: return "CELL_SYSMODULE_SYSUTIL_OSK_EXT"; - case 0x003c: return "CELL_SYSMODULE_VDEC_DIVX"; - case 0x003d: return "CELL_SYSMODULE_JPGENC"; - case 0x003e: return "CELL_SYSMODULE_SYSUTIL_GAME"; - case 0x003f: return "CELL_SYSMODULE_BGDL"; - case 0x0040: return "CELL_SYSMODULE_FREETYPE_TT"; - case 0x0041: return "CELL_SYSMODULE_SYSUTIL_VIDEO_UPLOAD"; - case 0x0042: return "CELL_SYSMODULE_SYSUTIL_SYSCONF_EXT"; - case 0x0043: return "CELL_SYSMODULE_FIBER"; - case 0x0044: return "CELL_SYSMODULE_SYSUTIL_NP_COMMERCE2"; - case 0x0045: return "CELL_SYSMODULE_SYSUTIL_NP_TUS"; - case 0x0046: return "CELL_SYSMODULE_VOICE"; - case 0x0047: return "CELL_SYSMODULE_ADEC_CELP8"; - case 0x0048: return "CELL_SYSMODULE_CELP8ENC"; - case 0x0049: return "CELL_SYSMODULE_SYSUTIL_LICENSEAREA"; - case 0x004a: return "CELL_SYSMODULE_SYSUTIL_MUSIC2"; - // TODO: Check if those libad are correctly matched. - // They belong to those IDs but actual order is unknown. - case 0x004b: return "CELL_SYSMODULE_AD_CORE"; - case 0x004c: return "CELL_SYSMODULE_AD_ASYNC"; - case 0x004d: return "CELL_SYSMODULE_AD_BILLBOARD_UTIL"; - case 0x004e: return "CELL_SYSMODULE_SYSUTIL_SCREENSHOT"; - case 0x004f: return "CELL_SYSMODULE_SYSUTIL_MUSIC_DECODE"; - case 0x0050: return "CELL_SYSMODULE_SPURS_JQ"; - case 0x0052: return "CELL_SYSMODULE_PNGENC"; - case 0x0053: return "CELL_SYSMODULE_SYSUTIL_MUSIC_DECODE2"; - case 0x0054: return "CELL_SYSMODULE_MEDI"; - case 0x0055: return "CELL_SYSMODULE_SYNC2"; - case 0x0056: return "CELL_SYSMODULE_SYSUTIL_NP_UTIL"; - case 0x0057: return "CELL_SYSMODULE_RUDP"; - case 0x0059: return "CELL_SYSMODULE_SYSUTIL_NP_SNS"; - case 0x005a: return "CELL_SYSMODULE_GEM"; - case 0x005c: return "CELL_SYSMODULE_SYSUTIL_CROSS_CONTROLLER"; - case 0xf00a: return "CELL_SYSMODULE_CELPENC"; - case 0xf010: return "CELL_SYSMODULE_GIFDEC"; - case 0xf019: return "CELL_SYSMODULE_ADEC_CELP"; - case 0xf01b: return "CELL_SYSMODULE_ADEC_M2BC"; - case 0xf01d: return "CELL_SYSMODULE_ADEC_M4AAC"; - case 0xf01e: return "CELL_SYSMODULE_ADEC_MP3"; - case 0xf023: return "CELL_SYSMODULE_IMEJP"; - case 0xf028: return "CELL_SYSMODULE_SYSUTIL_MUSIC"; - case 0xf029: return "CELL_SYSMODULE_PHOTO_EXPORT"; - case 0xf02a: return "CELL_SYSMODULE_PRINT"; - case 0xf02b: return "CELL_SYSMODULE_PHOTO_IMPORT"; - case 0xf02c: return "CELL_SYSMODULE_MUSIC_EXPORT"; - case 0xf02e: return "CELL_SYSMODULE_PHOTO_DECODE"; - case 0xf02f: return "CELL_SYSMODULE_SYSUTIL_SEARCH"; - case 0xf030: return "CELL_SYSMODULE_SYSUTIL_AVCHAT2"; - case 0xf034: return "CELL_SYSMODULE_SAIL_REC"; - case 0xf035: return "CELL_SYSMODULE_SYSUTIL_NP_TROPHY"; - case 0xf044: return "CELL_SYSMODULE_SYSUTIL_NP_EULA"; - case 0xf053: return "CELL_SYSMODULE_ADEC_AT3MULTI"; - case 0xf054: return "CELL_SYSMODULE_LIBATRAC3MULTI"; - case 0xffff: return "CELL_SYSMODULE_INVALID"; + case CELL_SYSMODULE_LV2DBG: + { + // If the LSB of the u64 at offset 0x18 and the extra load flag "EnableLv2ExceptionHandler" is set, + // or a certain bit in bootflag.dat, loads liblv2dbg_for_dex.sprx instead of liblv2dbg_for_cex + const vm::var boot_flag(8); + std::memset(boot_flag.get_ptr(), 0, boot_flag.get_count()); + + if (ret != CELL_OK || !read_from_ptr>(paramsfo.get_ptr(), 0x18)) + { + const vm::var file_descriptor; + + if (sys_fs_open(ppu, vm::make_str("/dev_hdd0/data/bootflag.dat"), 0, file_descriptor, 0, vm::null, 0) != CELL_OK) + { + return true; + } + + const vm::var nread; + ensure(sys_fs_read(ppu, *file_descriptor, boot_flag, 8, nread) == CELL_OK && *nread == 8ull); // Not checked on LLE + ensure(sys_fs_close(ppu, *file_descriptor) == CELL_OK); // Not checked on LLE + + return !!read_from_ptr>(boot_flag.get_ptr(), 1); + } + + return !read_from_ptr>(paramsfo.get_ptr(), 0x10); + } + case CELL_SYSMODULE_GCM_SYS: + { + // If the extra load flag "EnableGCMDebug" is set, loads libgcm_sys_deh.sprx instead of libgcm_sys.sprx + return ret != CELL_OK || !read_from_ptr>(paramsfo.get_ptr(), 0x10); + } + case CELL_SYSMODULE_FS: + { + // "F.E.A.R. First Encounter Assault Recon" needs an older version of cellFs + return std::ranges::contains(std::array{ "BLUS30003", "BLES00035", "BLES00036" }, std::string_view{ paramsfo.get_ptr() + 1, 9 }); + } + case CELL_SYSMODULE_SAIL: + { + // "Armored Core 4" needs a frame buffer release delay of three frames + return std::ranges::contains(std::array{ "BLJM60012", "BLES00039", "BLUS30027", "BLKS20001" }, std::string_view{ paramsfo.get_ptr() + 1, 9 }); + } + default: + std::unreachable(); + } +} + +static error_code load_module(ppu_thread& ppu, u8 module_idx, u32 args, vm::ptr argp) +{ + cellSysmodule.notice("load_module(module_idx=%d, args=%d, argp=*0x%x)", module_idx, args, argp); + + ensure(module_idx < MODULE_INFOS.size()); + auto& [loaded_count, prx_id] = s_sysmodule_context->module_states[module_idx]; + const auto& [path, dependencies] = MODULE_INFOS[module_idx]; + + cellSysmodule.notice("load_module(): path=\"%s\", loaded_count=%d, prx_id=%d", path, loaded_count, prx_id); + + if (path.empty()) + { + return CELL_OK; } - std::snprintf(tls_id_name, sizeof(tls_id_name), "0x%04X", id); - return tls_id_name; + if (loaded_count == smax + || ppu_execute<&sys_lwmutex_lock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex), 0) != CELL_OK) + { + return CELL_SYSMODULE_ERROR_FATAL; + } + + if (loaded_count == 0) + { + const vm::var module_path(72); + std::memcpy(module_path.get_ptr(), s_sysmodule_context->module_base_path, s_sysmodule_context->module_base_path_size); + std::memcpy(module_path.get_ptr() + s_sysmodule_context->module_base_path_size, path.data(), path.size() + 1); + + const error_code ret = s_sysmodule_context->use_mem_container + ? ppu_execute<&sys_prx_load_module_on_memcontainer>(ppu, +module_path, s_sysmodule_context->mem_container_id, 0, vm::null) + : ppu_execute<&sys_prx_load_module>(ppu, +module_path, 0, vm::null); + + prx_id = ret; + + if (ret < 1) + { + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE + return ret; + } + + if (const error_code ret = ppu_execute<&sys_prx_start_module>(ppu, prx_id, args, argp, +vm::make_var(0), 0, vm::null); ret != CELL_OK) + { + ensure(ppu_execute<&sys_prx_unload_module>(ppu, prx_id, 0, vm::null) == CELL_OK); // Not checked on LLE + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE + return ret; + } + } + + loaded_count++; + + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE + return CELL_OK; +} + +template +static error_code unload_module(ppu_thread& ppu, u8 module_idx) +{ + cellSysmodule.notice("unload_module(module_idx=%d)", module_idx); + + ensure(module_idx < MODULE_INFOS.size()); + auto& [loaded_count, prx_id] = s_sysmodule_context->module_states[module_idx]; + const std::string_view& path = MODULE_INFOS[module_idx].path; + + cellSysmodule.notice("unload_module(): path=\"%s\", loaded_count=%d, prx_id=%d", path, loaded_count, prx_id); + + if (path.empty()) + { + return CELL_OK; + } + + if (ppu_execute<&sys_lwmutex_lock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex), 0) != CELL_OK) + { + return CELL_SYSMODULE_ERROR_FATAL; + } + + if (loaded_count == 1 || force_unload) + { + ensure(ppu_execute<&sys_prx_stop_module>(ppu, prx_id, 0, vm::null, +vm::make_var(0), 0, vm::null) == CELL_OK); // Not checked on LLE + ensure(ppu_execute<&sys_prx_unload_module>(ppu, prx_id, 0, vm::null) == CELL_OK); // Not checked on LLE + loaded_count = 0; + } + else if (loaded_count > 1) + { + loaded_count--; + } + + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE + return CELL_OK; +} + +extern error_code sysmoduleModuleStop(ppu_thread& ppu) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellSysmodule.notice("module_stop()"); + + if (s_sysmodule_context->unk_addr) + { + if (sys_memory_free(ppu, s_sysmodule_context->unk_addr) != CELL_OK) + { + return 1; // SYS_PRX_STOP_FAILED + } + + s_sysmodule_context->unk_addr = 0; + } + + if (ppu_execute<&sys_lwmutex_destroy>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) != CELL_OK) + { + return 1; // SYS_PRX_STOP_FAILED + } + + return CELL_OK; // SYS_PRX_STOP_OK +} + +extern error_code sysmoduleModuleStart(ppu_thread& ppu, u32 args, vm::ptr argp) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellSysmodule.notice("module_start(args=%d, argp=*0x%x)", args, argp); + + s_sysmodule_context->use_mem_container = false; + + if (const vm::var auth_id; sys_ss_access_control_engine(1, sys_process_getpid(), auth_id.addr()) == CELL_OK) + { + if ((*auth_id & 0x00ffffff) == 1 || (*auth_id & 0x00ffffff) == 2) + { + return 1; // SYS_PRX_NO_RESIDENT + } + } + else if (sys_ss_access_control_engine(2, auth_id.addr(), 0) != CELL_OK || *auth_id == PAID_44 || *auth_id == 0x1070000056000001ll) + { + return 1; // SYS_PRX_NO_RESIDENT + } + + const vm::var lwmutex_attr + {{ + .protocol = SYS_SYNC_PRIORITY, + .recursive = SYS_SYNC_NOT_RECURSIVE, + .name_u64 = "_smolwm"_u64 + }}; + + if (ppu_execute<&sys_lwmutex_create>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex), +lwmutex_attr) != CELL_OK) + { + return 1; // SYS_PRX_NO_RESIDENT + } + + // TODO replace with proper struct + const vm::var paramsfo(0x40); + std::memset(paramsfo.get_ptr(), 0, paramsfo.get_count()); + + error_code get_paramsfo_ret = ppu_execute<&sys_process_get_paramsfo>(ppu, +paramsfo); + const bool retail_gcm_sys = check_special_handling(ppu); + + // Load default modules + if (const vm::var sdk_version; + ensure(sys_process_get_sdk_version(sys_process_getpid(), sdk_version) == CELL_OK), // Not checked on LLE + *sdk_version < 0x330000) + { + for (const u16 module_id : DEFAULT_MODULES | std::views::take(5) ) + { + if (module_id == CELL_SYSMODULE_GCM_SYS) + { + if (get_paramsfo_ret == CELL_OK && read_from_ptr>(paramsfo.get_ptr(), 0x10) && !retail_gcm_sys) + { + if (load_module(ppu, GCM_SYS_DEH_IDX, 8, paramsfo + 0x28) != CELL_OK) + { + sysmoduleModuleStop(ppu); + return 1; // SYS_PRX_NO_RESIDENT + } + } + else + { + if (load_module(ppu, GCM_SYS_DEH_IDX + retail_gcm_sys, 0, vm::null) != CELL_OK) + { + sysmoduleModuleStop(ppu); + return 1; // SYS_PRX_NO_RESIDENT + } + } + + s_sysmodule_context->module_states[CELL_SYSMODULE_GCM_SYS].loaded_count = + s_sysmodule_context->module_states[GCM_SYS_DEH_IDX + retail_gcm_sys].loaded_count; + } + else + { + if (cellSysmoduleLoadModule(ppu, module_id) != CELL_OK) + { + sysmoduleModuleStop(ppu); + return 1; // SYS_PRX_NO_RESIDENT + } + } + } + } + else + { + // Build a path list of the default modules + const vm::var paths(PATH_MAX_SIZE * size32(DEFAULT_MODULES)); + const vm::var[]> path_list(size32(DEFAULT_MODULES)); + + for (u32 i = 0; i < DEFAULT_MODULES.size(); i++) + { + const std::string_view path = DEFAULT_MODULES[i] == CELL_SYSMODULE_GCM_SYS + ? MODULE_INFOS[GCM_SYS_DEH_IDX + retail_gcm_sys].path + : DEFAULT_MODULES[i] >= INTERNAL_MODULE_ID_BASE + ? MODULE_INFOS[INTERNAL_MODULES_OFFSET + (DEFAULT_MODULES[i] & INTERNAL_MODULE_ID_MASK)].path + : MODULE_INFOS[DEFAULT_MODULES[i]].path; + + std::memcpy(paths.get_ptr() + (i * PATH_MAX_SIZE), MODULE_BASE_PATH.data(), MODULE_BASE_PATH.size()); + std::memcpy(paths.get_ptr() + (i * PATH_MAX_SIZE) + MODULE_BASE_PATH.size(), path.data(), path.size() + 1); + + path_list[i] = paths + i * PATH_MAX_SIZE; + } + + // Load them all at once + const vm::var id_list(size32(DEFAULT_MODULES)); + + if (ppu_execute<&sys_prx_load_module_list>(ppu, DEFAULT_MODULES.size(), +path_list, 0, vm::null, +id_list) != CELL_OK) + { + sysmoduleModuleStop(ppu); + return 1; // SYS_PRX_NO_RESIDENT + } + + // Start the modules + for (const auto [module_id, prx_ix] : std::views::zip(DEFAULT_MODULES, std::span{ id_list.begin().get_ptr(), id_list.get_count() })) + { + if (module_id == CELL_SYSMODULE_GCM_SYS && read_from_ptr>(paramsfo.get_ptr(), 0x10) && !retail_gcm_sys) + { + s_sysmodule_context->module_states[CELL_SYSMODULE_GCM_SYS].prx_id = static_cast(prx_ix); + + if (ppu_execute<&sys_prx_start_module>(ppu, +prx_ix, 8, paramsfo + 0x28, +vm::make_var(0), 0, vm::null) != CELL_OK) + { + sysmoduleModuleStop(ppu); + return 1; // SYS_PRX_NO_RESIDENT + } + + s_sysmodule_context->module_states[CELL_SYSMODULE_GCM_SYS].loaded_count++; + } + else + { + const u8 module_idx = module_id >= INTERNAL_MODULE_ID_BASE ? INTERNAL_MODULES_OFFSET + (module_id & INTERNAL_MODULE_ID_MASK) : module_id; + + s_sysmodule_context->module_states[module_idx].prx_id = static_cast(prx_ix); + + if (ppu_execute<&sys_prx_start_module>(ppu, +prx_ix, 0, vm::null, +vm::make_var(0), 0, vm::null) != CELL_OK) + { + sysmoduleModuleStop(ppu); + return 1;// SYS_PRX_NO_RESIDENT + } + + s_sysmodule_context->module_states[module_idx].loaded_count++; + } + + if (module_id == CELL_SYSMODULE_GCM_SYS) + { + s_sysmodule_context->module_states[GCM_SYS_DEH_IDX + retail_gcm_sys].loaded_count = + s_sysmodule_context->module_states[CELL_SYSMODULE_GCM_SYS].loaded_count; + } + else if (module_id == CELL_SYSMODULE_FS) + { + s_sysmodule_context->module_states[FS_IDX + check_special_handling(ppu)].loaded_count = + s_sysmodule_context->module_states[CELL_SYSMODULE_FS].loaded_count; + } + } + + // LLE overwrites the return value of sys_process_get_paramsfo() here and won't return an error if the allocation fails + get_paramsfo_ret = sys_memory_allocate(ppu, (!retail_gcm_sys + 1) * 0x10000ull, SYS_MEMORY_PAGE_SIZE_64K, s_sysmodule_context.ptr(&sysmodule_context::unk_addr)); + } + + if (get_paramsfo_ret != CELL_OK) + { + return CELL_OK; // SYS_PRX_RESIDENT + } + + // Load additional modules if certain ExtraLoadFlags are set + + if (read_from_ptr>(paramsfo.get_ptr(), 0x10) && !retail_gcm_sys && read_from_ptr>(paramsfo.get_ptr(), 0x18)) + { + cellSysmoduleSetDebugmode(true); + + if (load_module(ppu, GPAD_IDX, 8, paramsfo + 0x28) != CELL_OK) + { + ensure(unload_module(ppu, GPAD_IDX) == CELL_OK); // Not checked on LLE + } + + cellSysmoduleSetDebugmode(false); + } + + if (read_from_ptr>(paramsfo.get_ptr(), 0x10) && read_from_ptr>(paramsfo.get_ptr(), 0x18)) + { + if (load_module(ppu, PROF_IDX, 0, vm::null) != CELL_OK) + { + ensure(unload_module(ppu, PROF_IDX) == CELL_OK); // Not checked on LLE + } + } + + if (read_from_ptr>(paramsfo.get_ptr(), 0x10)) + { + if (load_module(ppu, LV2COREDUMP_IDX, 0, vm::null) != CELL_OK) + { + ensure(unload_module(ppu, LV2COREDUMP_IDX) == CELL_OK); // Not checked on LLE + } + } + + if (read_from_ptr>(paramsfo.get_ptr(), 0x10)) + { + if (cellSysmoduleLoadModuleInternal(ppu, 0xf022) != CELL_OK) + { + sysmoduleModuleStop(ppu); + return 1; // SYS_PRX_NO_RESIDENT + } + } + + return CELL_OK; // SYS_PRX_RESIDENT } error_code cellSysmoduleInitialize() { - cellSysmodule.warning("cellSysmoduleInitialize()"); + cellSysmodule.notice("cellSysmoduleInitialize()"); + // Doesn't do anything return CELL_OK; } error_code cellSysmoduleFinalize() { - cellSysmodule.warning("cellSysmoduleFinalize()"); + cellSysmodule.notice("cellSysmoduleFinalize()"); + // Doesn't do anything return CELL_OK; } -error_code cellSysmoduleSetMemcontainer(u32 ct_id) +error_code cellSysmoduleSetMemcontainer(ppu_thread& ppu, u32 ct_id) { - cellSysmodule.todo("cellSysmoduleSetMemcontainer(ct_id=0x%x)", ct_id); + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellSysmodule.notice("cellSysmoduleSetMemcontainer(ct_id=0x%x)", ct_id); + + if (ppu_execute<&sys_lwmutex_lock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex), 0) != CELL_OK) + { + return CELL_SYSMODULE_ERROR_FATAL; + } + + if (ct_id == SYS_MEMORY_CONTAINER_ID_INVALID) + { + s_sysmodule_context->use_mem_container = false; + } + else + { + if (const vm::var memory_info; + sys_memory_container_get_size(ppu, +memory_info, ct_id) != CELL_OK || memory_info->available_user_memory < 0x60000) + { + s_sysmodule_context->use_mem_container = false; + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE + return CELL_SYSMODULE_ERROR_INVALID_MEMCONTAINER; + } + + s_sysmodule_context->use_mem_container = true; + s_sysmodule_context->mem_container_id = ct_id; + } + + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE return CELL_OK; } -error_code cellSysmoduleLoadModule(u16 id) +error_code cellSysmoduleLoadModule(ppu_thread& ppu, u16 id) { - cellSysmodule.warning("cellSysmoduleLoadModule(id=0x%04X=%s)", id, get_module_id(id)); + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; - const auto name = get_module_name(id); + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } - if (!name) + cellSysmodule.notice("cellSysmoduleLoadModule(id=0x%x)", id); + + if (id < INTERNAL_MODULES_OFFSET) + { + switch (id) + { + case CELL_SYSMODULE_FS: + { + const bool fs_155 = check_special_handling(ppu); + const error_code ret = load_module(ppu, FS_IDX + fs_155, 0, vm::null); + s_sysmodule_context->module_states[CELL_SYSMODULE_FS].loaded_count = s_sysmodule_context->module_states[FS_IDX + fs_155].loaded_count; + return ret; + } + case CELL_SYSMODULE_GCM_SYS: + { + const bool retail_gcm_sys = check_special_handling(ppu); + const error_code ret = load_module(ppu, GCM_SYS_DEH_IDX + retail_gcm_sys, 0, vm::null); + s_sysmodule_context->module_states[CELL_SYSMODULE_GCM_SYS].loaded_count = s_sysmodule_context->module_states[GCM_SYS_DEH_IDX + retail_gcm_sys].loaded_count; + return ret; + } + case CELL_SYSMODULE_SAIL: + { + if (check_special_handling(ppu)) + { + const vm::var sail_parameters(2); + sail_parameters[0] = 0x19; // Parameter type? Must be set to this, or else cellSail will not set the frame buffer release delay below (see module_start() of cellSail) + sail_parameters[1] = 3; // Delay the release of the frame buffer by three frames instead of two + return load_module(ppu, CELL_SYSMODULE_SAIL, 8, +sail_parameters); + } + + return load_module(ppu, CELL_SYSMODULE_SAIL, 0, vm::null); + } + case CELL_SYSMODULE_LV2DBG: + { + const bool cex_lv2dgb = check_special_handling(ppu); + const error_code ret = load_module(ppu, LV2_DBG_IDX + cex_lv2dgb, 0, vm::null); + s_sysmodule_context->module_states[CELL_SYSMODULE_LV2DBG].loaded_count = s_sysmodule_context->module_states[LV2_DBG_IDX + cex_lv2dgb].loaded_count; + return ret; + } + case 0x58: + { + return CELL_SYSMODULE_ERROR_UNKNOWN; + } + default: + { + // Load dependencies + for (const u16 module_id : MODULE_INFOS[id].dependencies) + { + if (module_id >= INTERNAL_MODULE_ID_BASE) + { + return CELL_SYSMODULE_ERROR_UNKNOWN; + } + + if (const error_code ret = cellSysmoduleLoadModule(ppu, module_id); + ret != CELL_OK && ret != static_cast(CELL_SYSMODULE_ERROR_DUPLICATED)) + { + return ret; + } + } + + return load_module(ppu, static_cast(id), 0, vm::null); + } + } + } + + if ((id & INTERNAL_MODULE_ID_MASK) >= INTERNAL_MODULES_COUNT + || !LOADABLE_INTERNAL_MODULES[id & INTERNAL_MODULE_ID_MASK]) { return CELL_SYSMODULE_ERROR_UNKNOWN; } - //if (Module<>* m = Emu.GetModuleManager().GetModuleById(id)) - //{ - // // CELL_SYSMODULE_ERROR_DUPLICATED shouldn't be returned - // m->Load(); - //} - - return CELL_OK; + return cellSysmoduleLoadModuleInternal(ppu, id); } -error_code cellSysmoduleUnloadModule(u16 id) +error_code cellSysmoduleUnloadModule(ppu_thread& ppu, u16 id) { - cellSysmodule.warning("cellSysmoduleUnloadModule(id=0x%04X=%s)", id, get_module_id(id)); + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; - const auto name = get_module_name(id); + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } - if (!name) + cellSysmodule.notice("cellSysmoduleUnloadModule(id=0x%x)", id); + + if (id == 0x58) { return CELL_SYSMODULE_ERROR_UNKNOWN; } - //if (Module<>* m = Emu.GetModuleManager().GetModuleById(id)) - //{ - // if (!m->IsLoaded()) - // { - // cellSysmodule.error("cellSysmoduleUnloadModule() failed: module not loaded (id=0x%04x)", id); - // return CELL_SYSMODULE_ERROR_FATAL; - // } + if (id < INTERNAL_MODULES_OFFSET) + { + const vm::var sdk_version; + ensure(sys_process_get_sdk_version(sys_process_getpid(), sdk_version) == CELL_OK); // Not checked on LLE - // m->Unload(); - //} + // Do not unload a default module + for (const u16 module_id : DEFAULT_MODULES | std::views::take(*sdk_version < 0x330000 ? 5 : DEFAULT_MODULES.size())) + { + if (ppu_execute<&sys_lwmutex_lock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex), 0) != CELL_OK) + { + return CELL_SYSMODULE_ERROR_FATAL; + } - return CELL_OK; -} + if (module_id == id && s_sysmodule_context->module_states[id].loaded_count == 1) + { + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE + return CELL_OK; + } -error_code cellSysmoduleIsLoaded(u16 id) -{ - cellSysmodule.warning("cellSysmoduleIsLoaded(id=0x%04X=%s)", id, get_module_id(id)); + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE + } - const auto name = get_module_name(id); + error_code ret; + switch (id) + { + case CELL_SYSMODULE_FS: + { + const bool fs_155 = check_special_handling(ppu); + ret = unload_module(ppu, FS_IDX + fs_155); + s_sysmodule_context->module_states[CELL_SYSMODULE_FS].loaded_count = s_sysmodule_context->module_states[FS_IDX + fs_155].loaded_count; + break; + } + case CELL_SYSMODULE_GCM_SYS: + { + const bool retail_gcm_sys = check_special_handling(ppu); + ret = unload_module(ppu, GCM_SYS_DEH_IDX + retail_gcm_sys); + s_sysmodule_context->module_states[CELL_SYSMODULE_GCM_SYS].loaded_count = s_sysmodule_context->module_states[GCM_SYS_DEH_IDX + retail_gcm_sys].loaded_count; + break; + } + case CELL_SYSMODULE_LV2DBG: + { + const bool cex_lv2dbg = check_special_handling(ppu); + ret = unload_module(ppu, LV2_DBG_IDX + cex_lv2dbg); + s_sysmodule_context->module_states[CELL_SYSMODULE_LV2DBG].loaded_count = s_sysmodule_context->module_states[LV2_DBG_IDX + cex_lv2dbg].loaded_count; + break; + } + default: + { + ret = unload_module(ppu, static_cast(id)); + } + } - if (!name) + if (ret < CELL_OK) + { + return ret; + } + + // Unload dependencies in reverse order + for (const u16 module_id : MODULE_INFOS[id].dependencies | std::views::reverse) + { + if (module_id >= INTERNAL_MODULE_ID_BASE) + { + return CELL_SYSMODULE_ERROR_UNKNOWN; + } + + if (const error_code ret = cellSysmoduleUnloadModule(ppu, module_id); ret != CELL_OK) + { + return ret; + } + } + + return CELL_OK; + } + + if ((id & INTERNAL_MODULE_ID_MASK) >= INTERNAL_MODULES_COUNT + || !LOADABLE_INTERNAL_MODULES[id & INTERNAL_MODULE_ID_MASK]) { return CELL_SYSMODULE_ERROR_UNKNOWN; } - //if (Module<>* m = Emu.GetModuleManager().GetModuleById(id)) - //{ - // if (!m->IsLoaded()) - // { - // cellSysmodule.warning("cellSysmoduleIsLoaded(): module not loaded (id=0x%04x)", id); - // return CELL_SYSMODULE_ERROR_UNLOADED; - // } - //} - - return CELL_SYSMODULE_LOADED; + return cellSysmoduleUnloadModuleInternal(ppu, id); } -error_code cellSysmoduleGetImagesize() +error_code cellSysmoduleIsLoaded(ppu_thread& ppu, u16 id) { - UNIMPLEMENTED_FUNC(cellSysmodule); + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellSysmodule.notice("cellSysmoduleIsLoaded(id=0x%x)", id); + + if (id == 0x58) + { + return CELL_SYSMODULE_ERROR_UNKNOWN; + } + + if (id < INTERNAL_MODULES_OFFSET) + { + if (ppu_execute<&sys_lwmutex_lock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex), 0) != CELL_OK) + { + return CELL_SYSMODULE_ERROR_FATAL; + } + + const u16 module_id = MODULE_INFOS[id].path.empty() ? *MODULE_INFOS[id].dependencies.rbegin() : id; + const s32 loaded_count = s_sysmodule_context->module_states[module_id].loaded_count; + + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE + + return loaded_count > 0 ? CELL_OK : not_an_error(CELL_SYSMODULE_ERROR_UNLOADED); + } + + if ((id & INTERNAL_MODULE_ID_MASK) >= INTERNAL_MODULES_COUNT + || !LOADABLE_INTERNAL_MODULES[id & INTERNAL_MODULE_ID_MASK]) + { + return CELL_SYSMODULE_ERROR_UNKNOWN; + } + + return cellSysmoduleIsLoadedEx(ppu, id); +} + +error_code cellSysmoduleGetImagesize(ppu_thread& ppu, u16 id, vm::ptr image_size) +{ + cellSysmodule.notice("cellSysmoduleGetImagesize(id=0x%x, image_size=*0x%x)", id, image_size); + + ensure(!!image_size); // Not checked on LLE + + *image_size = 0; + + if (id < UNK_MODULE_ID_BASE || (id & UNK_MODULE_ID_MASK) >= 2) + { + return CELL_SYSMODULE_ERROR_FATAL; + } + + const vm::var file_descriptor; + + if (const vm::var path = (id & UNK_MODULE_ID_MASK) == 0 ? vm::make_str("/dev_flash/sys/external/flashATRAC.pic") : vm::make_str(""); // The second string is empty on LLE + sys_fs_open(ppu, path, 0, file_descriptor, 0, vm::null, 0) != CELL_OK) + { + return CELL_SYSMODULE_ERROR_FATAL; + } + + const vm::var sb; + + if (sys_fs_fstat(ppu, *file_descriptor, sb) != CELL_OK) + { + ensure(sys_fs_close(ppu, *file_descriptor) == CELL_OK); // Not checked on LLE + return CELL_SYSMODULE_ERROR_FATAL; + } + + *image_size = static_cast(sb->size); + + ensure(sys_fs_close(ppu, *file_descriptor) == CELL_OK); // Not checked on LLE return CELL_OK; } -error_code cellSysmoduleFetchImage() +error_code cellSysmoduleFetchImage(ppu_thread& ppu, u16 id, vm::ptr image_buf, vm::ptr buf_size) { - UNIMPLEMENTED_FUNC(cellSysmodule); + cellSysmodule.notice("cellSysmoduleFetchImage(id=0x%x, image_buf=*0x%x, buf_size=*0x%x)", id, image_buf, buf_size); + + if (id < UNK_MODULE_ID_BASE) + { + return CELL_SYSMODULE_ERROR_FATAL; + } + + const vm::var file_descriptor; + + ensure(!!buf_size); // Not checked on LLE + + if (const vm::var path = (id & UNK_MODULE_ID_MASK) == 0 ? vm::make_str("/dev_flash/sys/external/flashATRAC.pic") : vm::make_str(""); // The second string is empty on LLE + (id & UNK_MODULE_ID_MASK) >= 2 || sys_fs_open(ppu, path, 0, file_descriptor, 0, vm::null, 0) != CELL_OK) + { + *buf_size = 0; + return CELL_SYSMODULE_ERROR_FATAL; + } + + const vm::var sb; + + if (sys_fs_fstat(ppu, *file_descriptor, sb) != CELL_OK) + { + ensure(sys_fs_close(ppu, *file_descriptor) == CELL_OK); // Not checked on LLE + *buf_size = 0; + return CELL_SYSMODULE_ERROR_FATAL; + } + + const u64 read_size = std::min(*buf_size, sb->size); + + if (const vm::var nread; sys_fs_read(ppu, *file_descriptor, image_buf, read_size, nread) != CELL_OK) + { + *buf_size = 0; + } + else + { + *buf_size = static_cast(*nread); + } + + ensure(sys_fs_close(ppu, *file_descriptor) == CELL_OK); // Not checked on LLE return CELL_OK; } -error_code cellSysmoduleUnloadModuleInternal() +error_code cellSysmoduleUnloadModuleInternal(ppu_thread& ppu, u16 id) { - UNIMPLEMENTED_FUNC(cellSysmodule); + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellSysmodule.notice("cellSysmoduleUnloadModuleInternal(id=0x%x)", id); + + if (id < INTERNAL_MODULE_ID_BASE || id == CELL_SYSMODULE_INVALID || (id & INTERNAL_MODULE_ID_MASK) >= INTERNAL_MODULES_COUNT) + { + return CELL_SYSMODULE_ERROR_UNKNOWN; + } + + if (const error_code ret = unload_module(ppu, INTERNAL_MODULES_OFFSET + (id & INTERNAL_MODULE_ID_MASK)); ret != CELL_OK) + { + return ret; + } + + // Unload dependencies in reverse order + for (const u16 module_id : MODULE_INFOS[INTERNAL_MODULES_OFFSET + (id & INTERNAL_MODULE_ID_MASK)].dependencies | std::views::reverse) + { + if (const error_code ret = module_id < INTERNAL_MODULE_ID_BASE ? cellSysmoduleUnloadModule(ppu, id) : cellSysmoduleUnloadModuleInternal(ppu, id); ret != CELL_OK) + { + return ret; + } + } + return CELL_OK; } -error_code cellSysmoduleLoadModuleInternal() +error_code cellSysmoduleLoadModuleInternal(ppu_thread& ppu, u16 id) { - UNIMPLEMENTED_FUNC(cellSysmodule); + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellSysmodule.notice("cellSysmoduleLoadModuleInternal(id=0x%x)", id); + + if (id < INTERNAL_MODULE_ID_BASE || id == CELL_SYSMODULE_INVALID || (id & INTERNAL_MODULE_ID_MASK) >= INTERNAL_MODULES_COUNT) + { + return CELL_SYSMODULE_ERROR_UNKNOWN; + } + + if (id == 0xf024 || id == 0xf03e || id == 0xf020) + { + // TODO replace with proper struct + const vm::var paramsfo(0x40); + std::memset(paramsfo.get_ptr(), 0, paramsfo.get_count()); + + if (ppu_execute<&sys_process_get_paramsfo>(ppu, +paramsfo) != CELL_OK + || (!read_from_ptr>(paramsfo.get_ptr(), 0x18) && !read_from_ptr>(paramsfo.get_ptr(), 0x30))) + { + return CELL_SYSMODULE_ERROR_UNKNOWN; + } + } + + // Load dependencies + for (const u16 module_id : MODULE_INFOS[INTERNAL_MODULES_OFFSET + (id & INTERNAL_MODULE_ID_MASK)].dependencies) + { + if (const error_code ret = module_id < INTERNAL_MODULE_ID_BASE ? cellSysmoduleLoadModule(ppu, id) : cellSysmoduleLoadModuleInternal(ppu, id); + ret != CELL_OK && ret != static_cast(CELL_SYSMODULE_ERROR_DUPLICATED)) + { + return ret; + } + } + + return load_module(ppu, INTERNAL_MODULES_OFFSET + (id & INTERNAL_MODULE_ID_MASK), 0, vm::null); +} + +error_code cellSysmoduleUnloadModuleEx(ppu_thread& ppu, u16 id) +{ + cellSysmodule.notice("cellSysmoduleUnloadModuleEx(id=0x%x)", id); + + if (id >= INTERNAL_MODULE_ID_BASE && (id & INTERNAL_MODULE_ID_MASK) < INTERNAL_MODULES_COUNT + && LOADABLE_INTERNAL_MODULES_EX[id & INTERNAL_MODULE_ID_MASK]) + { + return cellSysmoduleUnloadModuleInternal(ppu, id); + } + + return cellSysmoduleUnloadModuleInternal(ppu, CELL_SYSMODULE_INVALID); +} + +error_code cellSysmoduleLoadModuleEx(ppu_thread& ppu, u16 id) +{ + cellSysmodule.notice("cellSysmoduleLoadModuleEx(id=0x%x)", id); + + if (id >= INTERNAL_MODULE_ID_BASE && (id & INTERNAL_MODULE_ID_MASK) < INTERNAL_MODULES_COUNT + && LOADABLE_INTERNAL_MODULES_EX[id & INTERNAL_MODULE_ID_MASK]) + { + return cellSysmoduleLoadModuleInternal(ppu, id); + } + + return cellSysmoduleLoadModuleInternal(ppu, CELL_SYSMODULE_INVALID); +} + +error_code cellSysmoduleIsLoadedEx(ppu_thread& ppu, u16 id) +{ + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellSysmodule.notice("cellSysmoduleIsLoadedEx(id=0x%x)", id); + + if (id < INTERNAL_MODULE_ID_BASE || id == CELL_SYSMODULE_INVALID || (id & INTERNAL_MODULE_ID_MASK) >= INTERNAL_MODULES_COUNT) + { + return CELL_SYSMODULE_ERROR_UNKNOWN; + } + + if (ppu_execute<&sys_lwmutex_lock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex), 0) != CELL_OK) + { + return CELL_SYSMODULE_ERROR_FATAL; + } + + if (s_sysmodule_context->module_states[INTERNAL_MODULES_OFFSET + (id & INTERNAL_MODULE_ID_MASK)].loaded_count == 0) + { + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE + return not_an_error(CELL_SYSMODULE_ERROR_UNLOADED); + } + + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE return CELL_OK; } -error_code cellSysmoduleUnloadModuleEx() +error_code cellSysmoduleLoadModuleFile(ppu_thread& ppu, vm::cptr path, vm::ptr unk) { - UNIMPLEMENTED_FUNC(cellSysmodule); + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellSysmodule.notice("cellSysmoduleLoadModuleFile(path=*0x%x=%s, unk=*0x%x)", path, path, unk); + + if (ppu_execute<&sys_lwmutex_lock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex), 0) != CELL_OK) + { + return CELL_SYSMODULE_ERROR_FATAL; + } + + ensure(!!unk); // Not checked on LLE + + const error_code ret = ppu_execute<&sys_prx_load_module>(ppu, path, 0, vm::null); + unk->prx_id = ret; + unk->unk = -1; + + if (ret < 1) + { + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE + return CELL_SYSMODULE_ERROR_FATAL; + } + + const vm::var file_name(0x200); + const vm::var segments(10); + const vm::var prx_module_info + {{ + .size = sizeof(sys_prx_module_info_t), + .filename = file_name, + .filename_size = file_name.get_count(), + .segments = segments, + .segments_num = segments.get_count() + }}; + + static_cast(ppu_execute<&sys_prx_get_module_info>(ppu, +unk->prx_id, 0, +prx_module_info)); + // LLE does "for (int i = 0; i < 0; i++) ..." after this, where the name of the prx would have been compared to "cellATRAC3enc_Library" and "cellATRACXenc_Library" + + if (const error_code ret = ppu_execute<&sys_prx_start_module>(ppu, +unk->prx_id, 0, vm::null, +vm::make_var(0), 0, vm::null); ret != CELL_OK) + { + ensure(ppu_execute<&sys_prx_unload_module>(ppu, +unk->prx_id, 0, vm::null) == CELL_OK); // Not checked on LLE + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE + return ret; + } + + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE return CELL_OK; } -error_code cellSysmoduleLoadModuleEx() +error_code cellSysmoduleUnloadModuleFile(ppu_thread& ppu, vm::cptr unk) { - UNIMPLEMENTED_FUNC(cellSysmodule); + // Blocking savestate creation due to ppu_thread::fast_call() + const std::unique_lock savestate_lock{ g_fxo->get(), std::try_to_lock }; + + if (!savestate_lock) + { + ppu.state += cpu_flag::again; + return {}; + } + + cellSysmodule.notice("cellSysmoduleUnloadModuleFile(unk=*0x%x)", unk); + + if (ppu_execute<&sys_lwmutex_lock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex), 0) != CELL_OK) + { + return CELL_SYSMODULE_ERROR_FATAL; + } + + ensure(!!unk); // Not checked on LLE + + if (ppu_execute<&sys_prx_stop_module>(ppu, +unk->prx_id, 0, vm::null, +vm::make_var(0), 0, vm::null) != CELL_OK + || ppu_execute<&sys_prx_unload_module>(ppu, +unk->prx_id, 0, vm::null) != CELL_OK) + { + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE + return CELL_SYSMODULE_ERROR_FATAL; + } + + ensure(unk->unk >= -1); // If this is not the case, LLE accesses its equivalent of sysmodule_context::modules_states with a negative index + + ensure(ppu_execute<&sys_lwmutex_unlock>(ppu, s_sysmodule_context.ptr(&sysmodule_context::mutex)) == CELL_OK); // Not checked on LLE return CELL_OK; } -error_code cellSysmoduleIsLoadedEx() +error_code cellSysmoduleSetDebugmode(u32 debug_mode) { - UNIMPLEMENTED_FUNC(cellSysmodule); - return CELL_OK; -} + cellSysmodule.notice("cellSysmoduleSetDebugmode(debug_mode=%d)", debug_mode); -error_code cellSysmoduleLoadModuleFile() -{ - UNIMPLEMENTED_FUNC(cellSysmodule); - return CELL_OK; -} + if (debug_mode == 1) + { + std::memcpy(s_sysmodule_context->module_base_path, MODULE_BASE_PATH_DEBUG.data(), MODULE_BASE_PATH_DEBUG.size() + 1); + s_sysmodule_context->module_base_path_size = static_cast(MODULE_BASE_PATH_DEBUG.size()); + } + else + { + std::memcpy(s_sysmodule_context->module_base_path, MODULE_BASE_PATH.data(), MODULE_BASE_PATH.size() + 1); + s_sysmodule_context->module_base_path_size = static_cast(MODULE_BASE_PATH.size()); + } -error_code cellSysmoduleUnloadModuleFile() -{ - UNIMPLEMENTED_FUNC(cellSysmodule); - return CELL_OK; -} - -error_code cellSysmoduleSetDebugmode() -{ - UNIMPLEMENTED_FUNC(cellSysmodule); return CELL_OK; } error_code cellSysmoduleSetInternalmode() { - UNIMPLEMENTED_FUNC(cellSysmodule); + cellSysmodule.notice("cellSysmoduleSetInternalmode()"); + // Doesn't do anything + return CELL_OK; +} + +error_code cellSysmodule_0x59521326(vm::ptr output) +{ + cellSysmodule.notice("cellSysmodule_0x59521326(output=*0x%x)", output); + + const vm::var psid; + + if (!output || sys_ss_get_open_psid(psid) != CELL_OK) + { + return -22; + } + + std::swap(psid->high, psid->low); + + u8 iv[0x10]; + std::memcpy(iv, psid.get_ptr(), sizeof(iv)); + + ensure(vm::check_addr(output.addr(), vm::page_readable | vm::page_writable, 0x10)); + + aescbc128_decrypt(KEY_UNK.data(), iv, reinterpret_cast(psid.get_ptr()), output.get_ptr(), 0x10); + return CELL_OK; } DECLARE(ppu_module_manager::cellSysmodule)("cellSysmodule", []() { + REG_VAR(cellSysmodule, s_sysmodule_context).flag(MFF_HIDDEN).init = [] + { + s_sysmodule_context->unk_addr = 0; + s_sysmodule_context->use_mem_container = false; + std::ranges::fill(s_sysmodule_context->module_states, module_state{ .loaded_count = 0, .prx_id = -1 }); + cellSysmoduleSetDebugmode(false); + }; + REG_FUNC(cellSysmodule, cellSysmoduleInitialize); REG_FUNC(cellSysmodule, cellSysmoduleFinalize); REG_FUNC(cellSysmodule, cellSysmoduleSetMemcontainer); @@ -449,4 +1393,8 @@ DECLARE(ppu_module_manager::cellSysmodule)("cellSysmodule", []() REG_FUNC(cellSysmodule, cellSysmoduleUnloadModuleFile); REG_FUNC(cellSysmodule, cellSysmoduleSetDebugmode); REG_FUNC(cellSysmodule, cellSysmoduleSetInternalmode); + REG_FNID(cellSysmodule, 0x59521326, cellSysmodule_0x59521326); + + REG_HIDDEN_FUNC(sysmoduleModuleStart); + REG_HIDDEN_FUNC(sysmoduleModuleStop); }); diff --git a/rpcs3/Emu/Cell/Modules/cellSysmodule.h b/rpcs3/Emu/Cell/Modules/cellSysmodule.h new file mode 100644 index 0000000000..7f3fa0fc6e --- /dev/null +++ b/rpcs3/Emu/Cell/Modules/cellSysmodule.h @@ -0,0 +1,144 @@ +#pragma once + +// Error codes +enum CellSysmoduleError +{ + CELL_SYSMODULE_ERROR_DUPLICATED = 0x80012001, + CELL_SYSMODULE_ERROR_UNKNOWN = 0x80012002, + CELL_SYSMODULE_ERROR_UNLOADED = 0x80012003, + CELL_SYSMODULE_ERROR_INVALID_MEMCONTAINER = 0x80012004, + CELL_SYSMODULE_ERROR_FATAL = 0x800120ff, +}; + +enum CellSysmoduleModuleID : u16 +{ + CELL_SYSMODULE_NET = 0x00, + CELL_SYSMODULE_HTTP = 0x01, + CELL_SYSMODULE_HTTP_UTIL = 0x02, + CELL_SYSMODULE_SSL = 0x03, + CELL_SYSMODULE_HTTPS = 0x04, + CELL_SYSMODULE_VDEC = 0x05, + CELL_SYSMODULE_ADEC = 0x06, + CELL_SYSMODULE_DMUX = 0x07, + CELL_SYSMODULE_VPOST = 0x08, + CELL_SYSMODULE_RTC = 0x09, + CELL_SYSMODULE_SPURS = 0x0a, + CELL_SYSMODULE_OVIS = 0x0b, + CELL_SYSMODULE_SHEAP = 0x0c, + CELL_SYSMODULE_SYNC = 0x0d, + CELL_SYSMODULE_FS = 0x0e, + CELL_SYSMODULE_JPGDEC = 0x0f, + CELL_SYSMODULE_GCM_SYS = 0x10, + CELL_SYSMODULE_AUDIO = 0x11, + CELL_SYSMODULE_PAMF = 0x12, + CELL_SYSMODULE_ATRAC3PLUS = 0x13, + CELL_SYSMODULE_NETCTL = 0x14, + CELL_SYSMODULE_SYSUTIL = 0x15, + CELL_SYSMODULE_SYSUTIL_NP = 0x16, + CELL_SYSMODULE_IO = 0x17, + CELL_SYSMODULE_PNGDEC = 0x18, + CELL_SYSMODULE_FONT = 0x19, + CELL_SYSMODULE_FONTFT = 0x1a, + CELL_SYSMODULE_FREETYPE = 0x1b, + CELL_SYSMODULE_USBD = 0x1c, + CELL_SYSMODULE_SAIL = 0x1d, + CELL_SYSMODULE_L10N = 0x1e, + CELL_SYSMODULE_RESC = 0x1f, + CELL_SYSMODULE_DAISY = 0x20, + CELL_SYSMODULE_KEY2CHAR = 0x21, + CELL_SYSMODULE_MIC = 0x22, + CELL_SYSMODULE_CAMERA = 0x23, + CELL_SYSMODULE_VDEC_MPEG2 = 0x24, + CELL_SYSMODULE_VDEC_AVC = 0x25, + CELL_SYSMODULE_ADEC_LPCM = 0x26, + CELL_SYSMODULE_ADEC_AC3 = 0x27, + CELL_SYSMODULE_ADEC_ATX = 0x28, + CELL_SYSMODULE_ADEC_AT3 = 0x29, + CELL_SYSMODULE_DMUX_PAMF = 0x2a, + CELL_SYSMODULE_VDEC_AL = 0x2b, + CELL_SYSMODULE_ADEC_AL = 0x2c, + CELL_SYSMODULE_DMUX_AL = 0x2d, + CELL_SYSMODULE_LV2DBG = 0x2e, + // 0x2f + CELL_SYSMODULE_USBPSPCM = 0x30, + CELL_SYSMODULE_AVCONF_EXT = 0x31, + CELL_SYSMODULE_SYSUTIL_USERINFO = 0x32, + CELL_SYSMODULE_SYSUTIL_SAVEDATA = 0x33, + CELL_SYSMODULE_SUBDISPLAY = 0x34, + CELL_SYSMODULE_SYSUTIL_REC = 0x35, + CELL_SYSMODULE_VIDEO_EXPORT = 0x36, + CELL_SYSMODULE_SYSUTIL_GAME_EXEC = 0x37, + CELL_SYSMODULE_SYSUTIL_NP2 = 0x38, + CELL_SYSMODULE_SYSUTIL_AP = 0x39, + CELL_SYSMODULE_SYSUTIL_NP_CLANS = 0x3a, + CELL_SYSMODULE_SYSUTIL_OSK_EXT = 0x3b, + CELL_SYSMODULE_VDEC_DIVX = 0x3c, + CELL_SYSMODULE_JPGENC = 0x3d, + CELL_SYSMODULE_SYSUTIL_GAME = 0x3e, + CELL_SYSMODULE_BGDL = 0x3f, + CELL_SYSMODULE_FREETYPE_TT = 0x40, + CELL_SYSMODULE_SYSUTIL_VIDEO_UPLOAD = 0x41, + CELL_SYSMODULE_SYSUTIL_SYSCONF_EXT = 0x42, + CELL_SYSMODULE_FIBER = 0x43, + CELL_SYSMODULE_SYSUTIL_NP_COMMERCE2 = 0x44, + CELL_SYSMODULE_SYSUTIL_NP_TUS = 0x45, + CELL_SYSMODULE_VOICE = 0x46, + CELL_SYSMODULE_ADEC_CELP8 = 0x47, + CELL_SYSMODULE_CELP8ENC = 0x48, + CELL_SYSMODULE_SYSUTIL_LICENSEAREA = 0x49, + CELL_SYSMODULE_SYSUTIL_MUSIC2 = 0x4a, + // 0x4b + // 0x4c + // 0x4d + CELL_SYSMODULE_SYSUTIL_SCREENSHOT = 0x4e, + CELL_SYSMODULE_SYSUTIL_MUSIC_DECODE = 0x4f, + CELL_SYSMODULE_SPURS_JQ = 0x50, + // 0x51 + CELL_SYSMODULE_PNGENC = 0x52, + CELL_SYSMODULE_SYSUTIL_MUSIC_DECODE2 = 0x53, + // 0x54 + CELL_SYSMODULE_SYNC2 = 0x55, + CELL_SYSMODULE_SYSUTIL_NP_UTIL = 0x56, + CELL_SYSMODULE_RUDP = 0x57, + // 0x58 = Invalid + CELL_SYSMODULE_SYSUTIL_NP_SNS = 0x59, + CELL_SYSMODULE_GEM = 0x5a, + CELL_SYSMODULE_SYSUTIL_CROSS_CONTROLLER = 0x5c, + + // Internal modules + CELL_SYSMODULE_CELPENC = 0xf00a, + CELL_SYSMODULE_GIFDEC = 0xf010, + CELL_SYSMODULE_ADEC_CELP = 0xf019, + CELL_SYSMODULE_ADEC_M2BC = 0xf01b, + CELL_SYSMODULE_ADEC_M4AAC = 0xf01d, + CELL_SYSMODULE_ADEC_MP3 = 0xf01e, + CELL_SYSMODULE_IMEJP = 0xf023, + CELL_SYSMODULE_SYSUTIL_MUSIC = 0xf028, + CELL_SYSMODULE_PHOTO_EXPORT = 0xf029, + CELL_SYSMODULE_PRINT = 0xf02a, + CELL_SYSMODULE_PHOTO_IMPORT = 0xf02b, + CELL_SYSMODULE_MUSIC_EXPORT = 0xf02c, + CELL_SYSMODULE_PHOTO_DECODE = 0xf02e, + CELL_SYSMODULE_SYSUTIL_SEARCH = 0xf02f, + CELL_SYSMODULE_SYSUTIL_AVCHAT2 = 0xf030, + CELL_SYSMODULE_SAIL_REC = 0xf034, + CELL_SYSMODULE_SYSUTIL_NP_TROPHY = 0xf035, + CELL_SYSMODULE_LIBATRAC3MULTI = 0xf054, + + CELL_SYSMODULE_INVALID = 0xffff +}; + +struct SysmoduleUnk +{ + be_t prx_id; + be_t unk; +}; + +CHECK_SIZE_ALIGN(SysmoduleUnk, 8, 4); + +error_code cellSysmoduleLoadModule(ppu_thread& ppu, u16 id); +error_code cellSysmoduleLoadModuleInternal(ppu_thread& ppu, u16 id); +error_code cellSysmoduleUnloadModule(ppu_thread& ppu, u16 id); +error_code cellSysmoduleUnloadModuleInternal(ppu_thread& ppu, u16 id); +error_code cellSysmoduleIsLoadedEx(ppu_thread& ppu, u16 id); +error_code cellSysmoduleSetDebugmode(u32 debug_mode); diff --git a/rpcs3/Emu/Cell/Modules/sysPrxForUser.cpp b/rpcs3/Emu/Cell/Modules/sysPrxForUser.cpp index 483fcfe1e9..5b91733596 100644 --- a/rpcs3/Emu/Cell/Modules/sysPrxForUser.cpp +++ b/rpcs3/Emu/Cell/Modules/sysPrxForUser.cpp @@ -61,7 +61,7 @@ s32 sys_process_is_stack(u32 p) return (p >> 28) == 0xD; } -error_code sys_process_get_paramsfo(vm::ptr buffer) +error_code sys_process_get_paramsfo([[maybe_unused]] ppu_thread& ppu, vm::ptr buffer) { sysPrxForUser.warning("sys_process_get_paramsfo(buffer=*0x%x)", buffer); diff --git a/rpcs3/Emu/Cell/Modules/sysPrxForUser.h b/rpcs3/Emu/Cell/Modules/sysPrxForUser.h index 8870277d55..5a05e2c079 100644 --- a/rpcs3/Emu/Cell/Modules/sysPrxForUser.h +++ b/rpcs3/Emu/Cell/Modules/sysPrxForUser.h @@ -63,5 +63,18 @@ error_code sys_ppu_thread_create(ppu_thread& ppu, vm::ptr thread_id, u32 en error_code sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih); void sys_ppu_thread_exit(ppu_thread& CPU, u64 val); +error_code sys_process_get_paramsfo(ppu_thread& ppu, vm::ptr buffer); void sys_game_process_exitspawn(ppu_thread& ppu, vm::cptr path, vm::cpptr argv, vm::cpptr envp, u32 data, u32 data_size, s32 prio, u64 flags); void sys_game_process_exitspawn2(ppu_thread& ppu, vm::cptr path, vm::cpptr argv, vm::cpptr envp, u32 data, u32 data_size, s32 prio, u64 flags); + +struct sys_prx_load_module_option_t; +struct sys_prx_unload_module_option_t; +struct sys_prx_module_info_t; + +error_code sys_prx_load_module(ppu_thread& ppu, vm::cptr path, u64 flags, vm::ptr pOpt); +error_code sys_prx_load_module_on_memcontainer(ppu_thread& ppu, vm::cptr path, u32 mem_ct, u64 flags, vm::ptr pOpt); +error_code sys_prx_load_module_list(ppu_thread& ppu, s32 count, vm::cpptr path_list, u64 flags, vm::ptr pOpt, vm::ptr id_list); +error_code sys_prx_start_module(ppu_thread& ppu, u32 id, u32 args, vm::ptr argp, vm::ptr result, u64 flags, vm::ptr pOpt); +error_code sys_prx_stop_module(ppu_thread& ppu, u32 id, u32 args, vm::ptr argp, vm::ptr result, u64 flags, vm::ptr pOpt); +error_code sys_prx_unload_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr pOpt); +error_code sys_prx_get_module_info(ppu_thread& ppu, u32 id, u64 flags, vm::ptr info); diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.cpp b/rpcs3/Emu/Cell/lv2/sys_prx.cpp index 4775224eb1..564943db11 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_prx.cpp @@ -8,6 +8,7 @@ #include "Crypto/unself.h" #include "Loader/ELF.h" +#include "Emu/Cell/PPUFunction.h" #include "Emu/Cell/PPUThread.h" #include "Emu/Cell/ErrorCodes.h" #include "Crypto/unedat.h" @@ -115,7 +116,7 @@ extern const std::map g_prx_list { "libssl.sprx", 0 }, { "libsvc1d.sprx", 0 }, { "libsync2.sprx", 0 }, - { "libsysmodule.sprx", 0 }, + { "libsysmodule.sprx", 1 }, { "libsysutil.sprx", 1 }, { "libsysutil_ap.sprx", 1 }, { "libsysutil_authdialog.sprx", 1 }, @@ -177,6 +178,9 @@ extern const std::map g_prx_list bool ppu_register_library_lock(std::string_view libname, bool lock_lib); +extern error_code sysmoduleModuleStart(ppu_thread& ppu, u32 args, vm::ptr argp); +extern error_code sysmoduleModuleStop(ppu_thread& ppu); + static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr /*pOpt*/, fs::file src = {}, s64 file_offset = 0) { if (flags != 0) @@ -232,6 +236,12 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr(); + if (name == "libsysmodule.sprx") + { + prx->start = vm::cast(g_fxo->get().func_addr(FIND_FUNC(sysmoduleModuleStart))); + prx->stop = vm::cast(g_fxo->get().func_addr(FIND_FUNC(sysmoduleModuleStop))); + } + prx->name = std::move(name); prx->path = std::move(path); diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 770028936f..006a837182 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -960,6 +960,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 7280166c33..ecd0a3b425 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1914,6 +1914,9 @@ Emu\Cell\Modules + + Emu\Cell\Modules + Emu\Cell\Modules diff --git a/rpcs3/util/bit_set.hpp b/rpcs3/util/bit_set.hpp index 2fa3921630..e9502f2ba5 100644 --- a/rpcs3/util/bit_set.hpp +++ b/rpcs3/util/bit_set.hpp @@ -10,6 +10,7 @@ struct bit_set public: constexpr bit_set() noexcept : m_bitset() {} constexpr bit_set(usz val) noexcept : m_bitset(val) {} + explicit constexpr bit_set(auto&&... args) noexcept requires std::is_constructible_v, decltype(args)...> : m_bitset(std::forward(args)...) {} [[nodiscard]] bool test(usz pos, std::source_location src_loc = std::source_location::current()) const {