From 08fe66a97fc38fb78278f37e572550d40ffecfdb Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Tue, 4 Nov 2025 10:41:26 +0200 Subject: [PATCH 01/25] [Libs] Http HLE (part2) (#2762) * added sceHttpParseStatusLine * added sceHttpUriMerge * macOS fix * more macOS fix? * typo * Update src/core/libraries/network/http.cpp Co-authored-by: illusony <37698908+illusion0001@users.noreply.github.com> * some work on sceHttpGetStatusCode * more draft on GetStatusCode * added some debug info * fixup --------- Co-authored-by: illusony <37698908+illusion0001@users.noreply.github.com> --- src/core/libraries/network/http.cpp | 302 +++++++++++++++++++++++++--- src/core/libraries/network/http.h | 41 ++-- 2 files changed, 297 insertions(+), 46 deletions(-) diff --git a/src/core/libraries/network/http.cpp b/src/core/libraries/network/http.cpp index 63e82ce81..1ae48dfed 100644 --- a/src/core/libraries/network/http.cpp +++ b/src/core/libraries/network/http.cpp @@ -9,6 +9,35 @@ namespace Libraries::Http { +static bool g_isHttpInitialized = true; // TODO temp always inited + +void NormalizeAndAppendPath(char* dest, char* src) { + char* lastSlash; + u64 length; + + lastSlash = strrchr(dest, '/'); + if (lastSlash == NULL) { + length = strlen(dest); + dest[length] = '/'; + dest[length + 1] = '\0'; + } else { + lastSlash[1] = '\0'; + } + if (*src == '/') { + dest[0] = '\0'; + } + length = strnlen(dest, 0x3fff); + strncat(dest, src, 0x3fff - length); + return; +} + +int HttpRequestInternal_Acquire(HttpRequestInternal** outRequest, u32 requestId) { + return 0; // TODO dummy +} +int HttpRequestInternal_Release(HttpRequestInternal* request) { + return 0; // TODO dummy +} + int PS4_SYSV_ABI sceHttpAbortRequest() { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; @@ -34,8 +63,9 @@ int PS4_SYSV_ABI sceHttpAddQuery() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpAddRequestHeader() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); +int PS4_SYSV_ABI sceHttpAddRequestHeader(int id, const char* name, const char* value, s32 mode) { + LOG_ERROR(Lib_Http, "(STUBBED) called id= {} name = {} value = {} mode = {}", id, + std::string(name), std::string(value), mode); return ORBIS_OK; } @@ -84,8 +114,9 @@ int PS4_SYSV_ABI sceHttpCreateConnection() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpCreateConnectionWithURL() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); +int PS4_SYSV_ABI sceHttpCreateConnectionWithURL(int tmplId, const char* url, bool enableKeepalive) { + LOG_ERROR(Lib_Http, "(STUBBED) called tmpid = {} url = {} enableKeepalive = {}", tmplId, + std::string(url), enableKeepalive ? 1 : 0); return ORBIS_OK; } @@ -104,8 +135,10 @@ int PS4_SYSV_ABI sceHttpCreateRequest2() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpCreateRequestWithURL() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); +int PS4_SYSV_ABI sceHttpCreateRequestWithURL(int connId, s32 method, const char* url, + u64 contentLength) { + LOG_ERROR(Lib_Http, "(STUBBED) called connId = {} method = {} url={} contentLength={}", connId, + method, url, contentLength); return ORBIS_OK; } @@ -184,7 +217,7 @@ int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpGetAllResponseHeaders() { +int PS4_SYSV_ABI sceHttpGetAllResponseHeaders(int reqId, char** header, u64* headerSize) { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_FAIL; } @@ -254,12 +287,42 @@ int PS4_SYSV_ABI sceHttpGetResponseContentLength() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpGetStatusCode() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); +int PS4_SYSV_ABI sceHttpGetStatusCode(int reqId, int* statusCode) { + LOG_ERROR(Lib_Http, "(STUBBED) called reqId = {}", reqId); +#if 0 + if (!g_isHttpInitialized) + return ORBIS_HTTP_ERROR_BEFORE_INIT; + + if (statusCode == nullptr) + return ORBIS_HTTP_ERROR_INVALID_VALUE; + + int ret = 0; + // Lookup HttpRequestInternal by reqId + HttpRequestInternal* request = nullptr; + ret = HttpRequestInternal_Acquire(&request, reqId); + if (ret < 0) + return ret; + request->m_mutex.lock(); + if (request->state > 0x11) { + if (request->state == 0x16) { + ret = request->errorCode; + } else { + *statusCode = request->httpStatusCode; + ret = 0; + } + } else { + ret = ORBIS_HTTP_ERROR_BEFORE_SEND; + } + request->m_mutex.unlock(); + HttpRequestInternal_Release(request); + + return ret; +#else return ORBIS_OK; +#endif } -int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, std::size_t poolSize) { +int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, u64 poolSize) { LOG_ERROR(Lib_Http, "(DUMMY) called libnetMemId = {} libsslCtxId = {} poolSize = {}", libnetMemId, libsslCtxId, poolSize); // return a value >1 @@ -267,14 +330,104 @@ int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, std::size_t poolS return ++id; } -int PS4_SYSV_ABI sceHttpParseResponseHeader() { +int PS4_SYSV_ABI sceHttpParseResponseHeader(const char* header, u64 headerLen, const char* fieldStr, + const char** fieldValue, u64* valueLen) { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpParseStatusLine() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceHttpParseStatusLine(const char* statusLine, u64 lineLen, int32_t* httpMajorVer, + int32_t* httpMinorVer, int32_t* responseCode, + const char** reasonPhrase, u64* phraseLen) { + if (!statusLine) { + LOG_ERROR(Lib_Http, "Invalid response"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + if (!httpMajorVer || !httpMinorVer || !responseCode || !reasonPhrase || !phraseLen) { + LOG_ERROR(Lib_Http, "Invalid value"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_VALUE; + } + *httpMajorVer = 0; + *httpMinorVer = 0; + if (lineLen < 8) { + LOG_ERROR(Lib_Http, "Linelen is smaller than 8"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + if (strncmp(statusLine, "HTTP/", 5) != 0) { + LOG_ERROR(Lib_Http, "statusLine doesn't start with HTTP/"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + + u64 index = 5; + + if (!isdigit(statusLine[index])) { + LOG_ERROR(Lib_Http, "Invalid response"); + + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + + while (isdigit(statusLine[index])) { + *httpMajorVer = *httpMajorVer * 10 + (statusLine[index] - '0'); + index++; + } + + if (statusLine[index] != '.') { + LOG_ERROR(Lib_Http, "Invalid response"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + index++; + + if (!isdigit(statusLine[index])) { + LOG_ERROR(Lib_Http, "Invalid response"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + + while (isdigit(statusLine[index])) { + *httpMinorVer = *httpMinorVer * 10 + (statusLine[index] - '0'); + index++; + } + + if (statusLine[index] != ' ') { + LOG_ERROR(Lib_Http, "Invalid response"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + index++; + + // Validate and parse the 3-digit HTTP response code + if (lineLen - index < 3 || !isdigit(statusLine[index]) || !isdigit(statusLine[index + 1]) || + !isdigit(statusLine[index + 2])) { + LOG_ERROR(Lib_Http, "Invalid response"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + + *responseCode = (statusLine[index] - '0') * 100 + (statusLine[index + 1] - '0') * 10 + + (statusLine[index + 2] - '0'); + index += 3; + + if (statusLine[index] != ' ') { + LOG_ERROR(Lib_Http, "Invalid response"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + index++; + + // Set the reason phrase start position + *reasonPhrase = &statusLine[index]; + u64 phraseStart = index; + + while (index < lineLen && statusLine[index] != '\n') { + index++; + } + + // Determine the length of the reason phrase, excluding trailing \r if present + if (index == phraseStart) { + *phraseLen = 0; + } else { + *phraseLen = + (statusLine[index - 1] == '\r') ? (index - phraseStart - 1) : (index - phraseStart); + } + + // Return the number of bytes processed + return index + 1; } int PS4_SYSV_ABI sceHttpReadData() { @@ -317,8 +470,8 @@ int PS4_SYSV_ABI sceHttpsEnableOptionPrivate() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpSendRequest() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); +int PS4_SYSV_ABI sceHttpSendRequest(int reqId, const void* postData, u64 size) { + LOG_ERROR(Lib_Http, "(STUBBED) called reqId = {} size = {}", reqId, size); return ORBIS_OK; } @@ -548,7 +701,8 @@ int PS4_SYSV_ABI sceHttpUnsetEpoll() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpUriBuild() { +int PS4_SYSV_ABI sceHttpUriBuild(char* out, u64* require, u64 prepare, + const OrbisHttpUriElement* srcElement, u32 option) { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; } @@ -563,13 +717,97 @@ int PS4_SYSV_ABI sceHttpUriEscape() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpUriMerge() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceHttpUriMerge(char* mergedUrl, char* url, char* relativeUri, u64* require, + u64 prepare, u32 option) { + u64 requiredLength; + int returnValue; + u64 baseUrlLength; + u64 relativeUriLength; + u64 totalLength; + u64 combinedLength; + int parseResult; + u64 localSizeRelativeUri; + u64 localSizeBaseUrl; + OrbisHttpUriElement parsedUriElement; + + if (option != 0 || url == NULL || relativeUri == NULL) { + LOG_ERROR(Lib_Http, "Invalid value"); + return ORBIS_HTTP_ERROR_INVALID_VALUE; + } + + returnValue = sceHttpUriParse(NULL, url, NULL, &localSizeBaseUrl, 0); + if (returnValue < 0) { + LOG_ERROR(Lib_Http, "returning {:#x}", returnValue); + return returnValue; + } + + returnValue = sceHttpUriParse(NULL, relativeUri, NULL, &localSizeRelativeUri, 0); + if (returnValue < 0) { + LOG_ERROR(Lib_Http, "returning {:#x}", returnValue); + return returnValue; + } + + baseUrlLength = strnlen(url, 0x3fff); + relativeUriLength = strnlen(relativeUri, 0x3fff); + requiredLength = localSizeBaseUrl + 2 + (relativeUriLength + baseUrlLength) * 2; + + if (require) { + *require = requiredLength; + } + + if (mergedUrl == NULL) { + return ORBIS_OK; + } + + if (prepare < requiredLength) { + LOG_ERROR(Lib_Http, "Error Out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + totalLength = strnlen(url, 0x3fff); + baseUrlLength = strnlen(relativeUri, 0x3fff); + combinedLength = totalLength + 1 + baseUrlLength; + relativeUriLength = prepare - combinedLength; + + returnValue = + sceHttpUriParse(&parsedUriElement, relativeUri, mergedUrl + totalLength + baseUrlLength + 1, + &localSizeRelativeUri, relativeUriLength); + if (returnValue < 0) { + LOG_ERROR(Lib_Http, "returning {:#x}", returnValue); + return returnValue; + } + if (parsedUriElement.scheme == NULL) { + strncpy(mergedUrl, relativeUri, requiredLength); + if (require) { + *require = strnlen(relativeUri, 0x3fff) + 1; + } + return ORBIS_OK; + } + + returnValue = + sceHttpUriParse(&parsedUriElement, url, mergedUrl + totalLength + baseUrlLength + 1, + &localSizeBaseUrl, relativeUriLength); + if (returnValue < 0) { + LOG_ERROR(Lib_Http, "returning {:#x}", returnValue); + return returnValue; + } + + combinedLength += localSizeBaseUrl; + strncpy(mergedUrl + combinedLength, parsedUriElement.path, prepare - combinedLength); + NormalizeAndAppendPath(mergedUrl + combinedLength, relativeUri); + + returnValue = sceHttpUriBuild(mergedUrl, 0, ~(baseUrlLength + totalLength) + prepare, + &parsedUriElement, 0x3f); + if (returnValue >= 0) { + return ORBIS_OK; + } else { + LOG_ERROR(Lib_Http, "returning {:#x}", returnValue); + return returnValue; + } } int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool, - size_t* require, size_t prepare) { + u64* require, u64 prepare) { LOG_INFO(Lib_Http, "srcUri = {}", std::string(srcUri)); if (!srcUri) { LOG_ERROR(Lib_Http, "invalid url"); @@ -586,10 +824,10 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v } // Track the total required buffer size - size_t requiredSize = 0; + u64 requiredSize = 0; // Parse the scheme (e.g., "http:", "https:", "file:") - size_t schemeLength = 0; + u64 schemeLength = 0; while (srcUri[schemeLength] && srcUri[schemeLength] != ':') { if (!isalnum(srcUri[schemeLength])) { LOG_ERROR(Lib_Http, "invalid url"); @@ -611,7 +849,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v requiredSize += schemeLength + 1; // Move past the scheme and ':' character - size_t offset = schemeLength + 1; + u64 offset = schemeLength + 1; // Check if "//" appears after the scheme if (strncmp(srcUri + offset, "//", 2) == 0) { @@ -638,7 +876,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v // Parse the path (everything after the slashes) char* pathStart = (char*)srcUri + offset; - size_t pathLength = 0; + u64 pathLength = 0; while (pathStart[pathLength] && pathStart[pathLength] != '?' && pathStart[pathLength] != '#') { pathLength++; @@ -689,7 +927,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v hostStart++; } - size_t hostLength = 0; + u64 hostLength = 0; while (hostStart[hostLength] && hostStart[hostLength] != '/' && hostStart[hostLength] != '?' && hostStart[hostLength] != ':') { hostLength++; @@ -714,7 +952,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v // Parse the port (if present) if (hostStart[hostLength] == ':') { char* portStart = hostStart + hostLength + 1; - size_t portLength = 0; + u64 portLength = 0; while (portStart[portLength] && isdigit(portStart[portLength])) { portLength++; } @@ -754,7 +992,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v // Parse the path (if present) if (srcUri[offset] == '/') { char* pathStart = (char*)srcUri + offset; - size_t pathLength = 0; + u64 pathLength = 0; while (pathStart[pathLength] && pathStart[pathLength] != '?' && pathStart[pathLength] != '#') { pathLength++; @@ -780,7 +1018,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v // Parse the query (if present) if (srcUri[offset] == '?') { char* queryStart = (char*)srcUri + offset + 1; - size_t queryLength = 0; + u64 queryLength = 0; while (queryStart[queryLength] && queryStart[queryLength] != '#') { queryLength++; } @@ -805,7 +1043,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v // Parse the fragment (if present) if (srcUri[offset] == '#') { char* fragmentStart = (char*)srcUri + offset + 1; - size_t fragmentLength = 0; + u64 fragmentLength = 0; while (fragmentStart[fragmentLength]) { fragmentLength++; } @@ -833,12 +1071,12 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, size_t srcSize) { +int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, u64 srcSize) { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpUriUnescape(char* out, size_t* require, size_t prepare, const char* in) { +int PS4_SYSV_ABI sceHttpUriUnescape(char* out, u64* require, u64 prepare, const char* in) { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; } diff --git a/src/core/libraries/network/http.h b/src/core/libraries/network/http.h index 228080207..701bb0e05 100644 --- a/src/core/libraries/network/http.h +++ b/src/core/libraries/network/http.h @@ -3,6 +3,7 @@ #pragma once +#include #include "common/types.h" #include "core/libraries/network/ssl.h" @@ -25,6 +26,12 @@ struct OrbisHttpUriElement { u8 reserved[10]; }; +struct HttpRequestInternal { + int state; // +0x20 + int errorCode; // +0x28 + int httpStatusCode; // +0x20C + std::mutex m_mutex; +}; using OrbisHttpsCaList = Libraries::Ssl::OrbisSslCaList; int PS4_SYSV_ABI sceHttpAbortRequest(); @@ -32,7 +39,7 @@ int PS4_SYSV_ABI sceHttpAbortRequestForce(); int PS4_SYSV_ABI sceHttpAbortWaitRequest(); int PS4_SYSV_ABI sceHttpAddCookie(); int PS4_SYSV_ABI sceHttpAddQuery(); -int PS4_SYSV_ABI sceHttpAddRequestHeader(); +int PS4_SYSV_ABI sceHttpAddRequestHeader(int id, const char* name, const char* value, s32 mode); int PS4_SYSV_ABI sceHttpAddRequestHeaderRaw(); int PS4_SYSV_ABI sceHttpAuthCacheExport(); int PS4_SYSV_ABI sceHttpAuthCacheFlush(); @@ -42,11 +49,12 @@ int PS4_SYSV_ABI sceHttpCookieExport(); int PS4_SYSV_ABI sceHttpCookieFlush(); int PS4_SYSV_ABI sceHttpCookieImport(); int PS4_SYSV_ABI sceHttpCreateConnection(); -int PS4_SYSV_ABI sceHttpCreateConnectionWithURL(); +int PS4_SYSV_ABI sceHttpCreateConnectionWithURL(int tmplId, const char* url, bool enableKeepalive); int PS4_SYSV_ABI sceHttpCreateEpoll(); int PS4_SYSV_ABI sceHttpCreateRequest(); int PS4_SYSV_ABI sceHttpCreateRequest2(); -int PS4_SYSV_ABI sceHttpCreateRequestWithURL(); +int PS4_SYSV_ABI sceHttpCreateRequestWithURL(int connId, s32 method, const char* url, + u64 contentLength); int PS4_SYSV_ABI sceHttpCreateRequestWithURL2(); int PS4_SYSV_ABI sceHttpCreateTemplate(); int PS4_SYSV_ABI sceHttpDbgEnableProfile(); @@ -62,7 +70,7 @@ int PS4_SYSV_ABI sceHttpDeleteRequest(); int PS4_SYSV_ABI sceHttpDeleteTemplate(); int PS4_SYSV_ABI sceHttpDestroyEpoll(); int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled(); -int PS4_SYSV_ABI sceHttpGetAllResponseHeaders(); +int PS4_SYSV_ABI sceHttpGetAllResponseHeaders(int reqId, char** header, u64* headerSize); int PS4_SYSV_ABI sceHttpGetAuthEnabled(); int PS4_SYSV_ABI sceHttpGetAutoRedirect(); int PS4_SYSV_ABI sceHttpGetConnectionStat(); @@ -76,10 +84,13 @@ int PS4_SYSV_ABI sceHttpGetMemoryPoolStats(); int PS4_SYSV_ABI sceHttpGetNonblock(); int PS4_SYSV_ABI sceHttpGetRegisteredCtxIds(); int PS4_SYSV_ABI sceHttpGetResponseContentLength(); -int PS4_SYSV_ABI sceHttpGetStatusCode(); -int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, std::size_t poolSize); -int PS4_SYSV_ABI sceHttpParseResponseHeader(); -int PS4_SYSV_ABI sceHttpParseStatusLine(); +int PS4_SYSV_ABI sceHttpGetStatusCode(int reqId, int* statusCode); +int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, u64 poolSize); +int PS4_SYSV_ABI sceHttpParseResponseHeader(const char* header, u64 headerLen, const char* fieldStr, + const char** fieldValue, u64* valueLen); +int PS4_SYSV_ABI sceHttpParseStatusLine(const char* statusLine, u64 lineLen, int32_t* httpMajorVer, + int32_t* httpMinorVer, int32_t* responseCode, + const char** reasonPhrase, u64* phraseLen); int PS4_SYSV_ABI sceHttpReadData(); int PS4_SYSV_ABI sceHttpRedirectCacheFlush(); int PS4_SYSV_ABI sceHttpRemoveRequestHeader(); @@ -88,7 +99,7 @@ int PS4_SYSV_ABI sceHttpsDisableOption(); int PS4_SYSV_ABI sceHttpsDisableOptionPrivate(); int PS4_SYSV_ABI sceHttpsEnableOption(); int PS4_SYSV_ABI sceHttpsEnableOptionPrivate(); -int PS4_SYSV_ABI sceHttpSendRequest(); +int PS4_SYSV_ABI sceHttpSendRequest(int reqId, const void* postData, u64 size); int PS4_SYSV_ABI sceHttpSetAcceptEncodingGZIPEnabled(); int PS4_SYSV_ABI sceHttpSetAuthEnabled(); int PS4_SYSV_ABI sceHttpSetAuthInfoCallback(); @@ -134,14 +145,16 @@ int PS4_SYSV_ABI sceHttpTerm(); int PS4_SYSV_ABI sceHttpTryGetNonblock(); int PS4_SYSV_ABI sceHttpTrySetNonblock(); int PS4_SYSV_ABI sceHttpUnsetEpoll(); -int PS4_SYSV_ABI sceHttpUriBuild(); +int PS4_SYSV_ABI sceHttpUriBuild(char* out, u64* require, u64 prepare, + const OrbisHttpUriElement* srcElement, u32 option); int PS4_SYSV_ABI sceHttpUriCopy(); int PS4_SYSV_ABI sceHttpUriEscape(); -int PS4_SYSV_ABI sceHttpUriMerge(); +int PS4_SYSV_ABI sceHttpUriMerge(char* mergedUrl, char* url, char* relativeUri, u64* require, + u64 prepare, u32 option); int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool, - size_t* require, size_t prepare); -int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, size_t srcSize); -int PS4_SYSV_ABI sceHttpUriUnescape(char* out, size_t* require, size_t prepare, const char* in); + u64* require, u64 prepare); +int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, u64 srcSize); +int PS4_SYSV_ABI sceHttpUriUnescape(char* out, u64* require, u64 prepare, const char* in); int PS4_SYSV_ABI sceHttpWaitRequest(); void RegisterLib(Core::Loader::SymbolsResolver* sym); From 683e5f3b0462f91cc7a80c6f688d5b83e51f32be Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Tue, 4 Nov 2025 02:57:26 -0600 Subject: [PATCH 02/25] Core: Simulate write-only file access with read-write access (#3360) * Swap write access mode for read write Opening with access mode w will erase the opened file. We do not want this. * Create mode Opening with write access was previously the only way to create a file through open, so add a separate FileAccessMode that uses the write access mode to create files. * Update file_system.cpp Remove a hack added to posix_rename to bypass the file clearing behaviors of FileAccessMode::Write * Check access mode in read functions Write-only files cause the EBADF return on the various read functions. Now that we're opening files differently, properly handling this is necessary. * Separate appends into proper modes Fixes a potential regression from one of my prior PRs, and ensures the Write | Append flag combo also behaves properly in read-related functions. * Move IsWriteOnly check after device/socket reads file->f is only valid for files, so checking this before checking for sockets/devices will cause access violations. * Fix issues Now that Write is identical to ReadWrite, internal uses of Write need to be swapped to my new Create mode * Fix remaining uses of FileAccessMode write to create files Missed these before. * Fix rebase * Add stubbed get_authinfo (#3722) * mostly stubbed get_authinfo * Return value observed on console if get_authinfo was called for the current thread, esrch otherwise --------- Co-authored-by: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Co-authored-by: georgemoralis --- src/common/io_file.cpp | 20 ++++---- src/common/io_file.h | 18 +++++-- src/common/logging/backend.cpp | 2 +- src/core/devtools/widget/common.h | 2 +- src/core/devtools/widget/frame_dump.cpp | 2 +- src/core/file_format/psf.cpp | 2 +- src/core/libraries/kernel/file_system.cpp | 47 ++++++++++++++----- .../libraries/save_data/save_instance.cpp | 2 +- src/core/libraries/save_data/save_memory.cpp | 4 +- src/core/libraries/save_data/savedata.cpp | 2 +- src/core/loader/elf.cpp | 8 ++-- src/core/loader/symbols_resolver.cpp | 2 +- .../frontend/translate/translate.cpp | 2 +- .../passes/flatten_extended_userdata_pass.cpp | 2 +- src/shader_recompiler/ir/program.cpp | 4 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 2 +- 16 files changed, 78 insertions(+), 43 deletions(-) diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp index 6fa9062a7..249d0e0f4 100644 --- a/src/common/io_file.cpp +++ b/src/common/io_file.cpp @@ -40,28 +40,30 @@ namespace { switch (mode) { case FileAccessMode::Read: return L"rb"; - case FileAccessMode::Write: - return L"wb"; case FileAccessMode::Append: return L"ab"; + case FileAccessMode::Write: case FileAccessMode::ReadWrite: return L"r+b"; case FileAccessMode::ReadAppend: return L"a+b"; + case FileAccessMode::Create: + return L"wb"; } break; case FileType::TextFile: switch (mode) { case FileAccessMode::Read: return L"r"; - case FileAccessMode::Write: - return L"w"; case FileAccessMode::Append: return L"a"; + case FileAccessMode::Write: case FileAccessMode::ReadWrite: return L"r+"; case FileAccessMode::ReadAppend: return L"a+"; + case FileAccessMode::Create: + return L"w"; } break; } @@ -91,28 +93,30 @@ namespace { switch (mode) { case FileAccessMode::Read: return "rb"; - case FileAccessMode::Write: - return "wb"; case FileAccessMode::Append: return "ab"; + case FileAccessMode::Write: case FileAccessMode::ReadWrite: return "r+b"; case FileAccessMode::ReadAppend: return "a+b"; + case FileAccessMode::Create: + return "wb"; } break; case FileType::TextFile: switch (mode) { case FileAccessMode::Read: return "r"; - case FileAccessMode::Write: - return "w"; case FileAccessMode::Append: return "a"; + case FileAccessMode::Write: case FileAccessMode::ReadWrite: return "r+"; case FileAccessMode::ReadAppend: return "a+"; + case FileAccessMode::Create: + return "w"; } break; } diff --git a/src/common/io_file.h b/src/common/io_file.h index c6eb3b563..d4e4b2e47 100644 --- a/src/common/io_file.h +++ b/src/common/io_file.h @@ -21,9 +21,8 @@ enum class FileAccessMode { */ Read = 1 << 0, /** - * If the file at path exists, the existing contents of the file are erased. - * The empty file is then opened for writing. - * If the file at path does not exist, it creates and opens a new empty file for writing. + * If the file at path exists, it opens the file for writing. + * If the file at path does not exist, it fails to open the file. */ Write = 1 << 1, /** @@ -42,6 +41,12 @@ enum class FileAccessMode { * reading and appending. */ ReadAppend = Read | Append, + /** + * If the file at path exists, the existing contents of the file are erased. + * The empty file is then opened for writing. + * If the file at path does not exist, it creates and opens a new empty file for writing. + */ + Create = 1 << 3, }; DECLARE_ENUM_FLAG_OPERATORS(FileAccessMode); @@ -102,6 +107,11 @@ public: return file != nullptr; } + bool IsWriteOnly() const { + return file_access_mode == FileAccessMode::Append || + file_access_mode == FileAccessMode::Write; + } + uintptr_t GetFileMapping(); int Open(const std::filesystem::path& path, FileAccessMode mode, @@ -210,7 +220,7 @@ public: } static size_t WriteBytes(const std::filesystem::path path, const auto& data) { - IOFile out(path, FileAccessMode::Write); + IOFile out(path, FileAccessMode::Create); return out.Write(data); } std::FILE* file = nullptr; diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index ce9386853..6b68651de 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -62,7 +62,7 @@ private: class FileBackend { public: explicit FileBackend(const std::filesystem::path& filename, bool should_append = false) - : file{filename, should_append ? FS::FileAccessMode::Append : FS::FileAccessMode::Write, + : file{filename, should_append ? FS::FileAccessMode::Append : FS::FileAccessMode::Create, FS::FileType::TextFile} {} ~FileBackend() = default; diff --git a/src/core/devtools/widget/common.h b/src/core/devtools/widget/common.h index 4684f6e3b..1c79ffcac 100644 --- a/src/core/devtools/widget/common.h +++ b/src/core/devtools/widget/common.h @@ -152,7 +152,7 @@ inline std::string RunDisassembler(const std::string& disassembler_cli, const T& } } else { cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\""); - Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write); + Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Create); file.Write(shader_code); file.Close(); diff --git a/src/core/devtools/widget/frame_dump.cpp b/src/core/devtools/widget/frame_dump.cpp index 1eaa78897..06b65b0ba 100644 --- a/src/core/devtools/widget/frame_dump.cpp +++ b/src/core/devtools/widget/frame_dump.cpp @@ -123,7 +123,7 @@ void FrameDumpViewer::Draw() { const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time, magic_enum::enum_name(selected_queue_type), selected_submit_num, selected_queue_num2); - Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write); + Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Create); const auto& data = frame_dump->queues[selected_cmd].data; if (file.IsOpen()) { DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname)); diff --git a/src/core/file_format/psf.cpp b/src/core/file_format/psf.cpp index 7e0ffc9a3..047828330 100644 --- a/src/core/file_format/psf.cpp +++ b/src/core/file_format/psf.cpp @@ -99,7 +99,7 @@ bool PSF::Open(const std::vector& psf_buffer) { } bool PSF::Encode(const std::filesystem::path& filepath) const { - Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Write); + Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Create); if (!file.IsOpen()) { return false; } diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 2c4b4a670..d5cbe6074 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -140,7 +140,7 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { return -1; } // Create a file if it doesn't exist - Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write); + Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Create); } } else if (!exists) { // If we're not creating a file, and it doesn't exist, return ENOENT @@ -205,22 +205,30 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { } if (read) { - // Read only + // Open exclusively for reading e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read); } else if (read_only) { // Can't open files with write/read-write access in a read only directory h->DeleteHandle(handle); *__Error() = POSIX_EROFS; return -1; - } else if (append) { - // Append can be specified with rdwr or write, but we treat it as a separate mode. - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); } else if (write) { - // Write only - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); + if (append) { + // Open exclusively for appending + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); + } else { + // Open exclusively for writing + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); + } } else if (rdwr) { // Read and write - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); + if (append) { + // Open for reading and appending + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadAppend); + } else { + // Open for reading and writing + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); + } } } @@ -354,6 +362,12 @@ s64 PS4_SYSV_ABI readv(s32 fd, const OrbisKernelIovec* iov, s32 iovcnt) { } return result; } + + if (file->f.IsWriteOnly()) { + *__Error() = POSIX_EBADF; + return -1; + } + s64 total_read = 0; for (s32 i = 0; i < iovcnt; i++) { total_read += ReadFile(file->f, iov[i].iov_base, iov[i].iov_len); @@ -509,6 +523,12 @@ s64 PS4_SYSV_ABI read(s32 fd, void* buf, u64 nbytes) { // Socket functions handle errnos internally. return file->socket->ReceivePacket(buf, nbytes, 0, nullptr, 0); } + + if (file->f.IsWriteOnly()) { + *__Error() = POSIX_EBADF; + return -1; + } + return ReadFile(file->f, buf, nbytes); } @@ -801,11 +821,7 @@ s32 PS4_SYSV_ABI posix_rename(const char* from, const char* to) { auto* h = Common::Singleton::Instance(); auto file = h->GetFile(src_path); if (file) { - // We need to force ReadWrite if the file had Write access before - // Otherwise f.Open will clear the file contents. - auto access_mode = file->f.GetAccessMode() == Common::FS::FileAccessMode::Write - ? Common::FS::FileAccessMode::ReadWrite - : file->f.GetAccessMode(); + auto access_mode = file->f.GetAccessMode(); file->f.Close(); std::filesystem::remove(src_path); file->f.Open(dst_path, access_mode); @@ -855,6 +871,11 @@ s64 PS4_SYSV_ABI posix_preadv(s32 fd, OrbisKernelIovec* iov, s32 iovcnt, s64 off return result; } + if (file->f.IsWriteOnly()) { + *__Error() = POSIX_EBADF; + return -1; + } + const s64 pos = file->f.Tell(); SCOPE_EXIT { file->f.Seek(pos); diff --git a/src/core/libraries/save_data/save_instance.cpp b/src/core/libraries/save_data/save_instance.cpp index 05253eb23..75a644fdb 100644 --- a/src/core/libraries/save_data/save_instance.cpp +++ b/src/core/libraries/save_data/save_instance.cpp @@ -180,7 +180,7 @@ void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_cor } if (!ignore_corrupt && !read_only) { - Common::FS::IOFile f(corrupt_file_path, Common::FS::FileAccessMode::Write); + Common::FS::IOFile f(corrupt_file_path, Common::FS::FileAccessMode::Create); f.Close(); } diff --git a/src/core/libraries/save_data/save_memory.cpp b/src/core/libraries/save_data/save_memory.cpp index 4080362eb..5f5ba8fea 100644 --- a/src/core/libraries/save_data/save_memory.cpp +++ b/src/core/libraries/save_data/save_memory.cpp @@ -59,7 +59,7 @@ void PersistMemory(u32 slot_id, bool lock) { while (n++ < 10) { try { IOFile f; - int r = f.Open(memoryPath, Common::FS::FileAccessMode::Write); + int r = f.Open(memoryPath, Common::FS::FileAccessMode::Create); if (f.IsOpen()) { f.WriteRaw(data.memory_cache.data(), data.memory_cache.size()); f.Close(); @@ -148,7 +148,7 @@ void SetIcon(u32 slot_id, void* buf, size_t buf_size) { fs::copy_file(src_icon, icon_path); } } else { - IOFile file(icon_path, Common::FS::FileAccessMode::Write); + IOFile file(icon_path, Common::FS::FileAccessMode::Create); file.WriteRaw(buf, buf_size); file.Close(); } diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index aec7c1d54..7fba8ed21 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -1389,7 +1389,7 @@ Error PS4_SYSV_ABI sceSaveDataSaveIcon(const OrbisSaveDataMountPoint* mountPoint } try { - const Common::FS::IOFile file(path, Common::FS::FileAccessMode::Write); + const Common::FS::IOFile file(path, Common::FS::FileAccessMode::Create); file.WriteRaw(icon->buf, std::min(icon->bufSize, icon->dataSize)); } catch (const fs::filesystem_error& e) { LOG_ERROR(Lib_SaveData, "Failed to load icon: {}", e.what()); diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 4de20436f..ed3f43c1c 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -502,19 +502,19 @@ bool Elf::IsSharedLib() { } void Elf::ElfHeaderDebugDump(const std::filesystem::path& file_name) { - Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write, + Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create, Common::FS::FileType::TextFile}; f.WriteString(ElfHeaderStr()); } void Elf::SelfHeaderDebugDump(const std::filesystem::path& file_name) { - Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write, + Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create, Common::FS::FileType::TextFile}; f.WriteString(SElfHeaderStr()); } void Elf::SelfSegHeaderDebugDump(const std::filesystem::path& file_name) { - Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write, + Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create, Common::FS::FileType::TextFile}; for (u16 i = 0; i < m_self.segment_count; i++) { f.WriteString(SELFSegHeader(i)); @@ -522,7 +522,7 @@ void Elf::SelfSegHeaderDebugDump(const std::filesystem::path& file_name) { } void Elf::PHeaderDebugDump(const std::filesystem::path& file_name) { - Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write, + Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create, Common::FS::FileType::TextFile}; if (m_elf_header.e_phentsize > 0) { for (u16 i = 0; i < m_elf_header.e_phnum; i++) { diff --git a/src/core/loader/symbols_resolver.cpp b/src/core/loader/symbols_resolver.cpp index 09c7fae8a..9fef1a2a2 100644 --- a/src/core/loader/symbols_resolver.cpp +++ b/src/core/loader/symbols_resolver.cpp @@ -32,7 +32,7 @@ const SymbolRecord* SymbolsResolver::FindSymbol(const SymbolResolver& s) const { } void SymbolsResolver::DebugDump(const std::filesystem::path& file_name) { - Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write, + Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create, Common::FS::FileType::TextFile}; for (const auto& symbol : m_symbols) { const auto ids = Common::SplitString(symbol.name, '#'); diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index 668882254..57b50a3e1 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -559,7 +559,7 @@ void Translator::EmitFetch(const GcnInst& inst) { std::filesystem::create_directories(dump_dir); } const auto filename = fmt::format("vs_{:#018x}.fetch.bin", info.pgm_hash); - const auto file = IOFile{dump_dir / filename, FileAccessMode::Write}; + const auto file = IOFile{dump_dir / filename, FileAccessMode::Create}; file.WriteRaw(fetch_data->code, fetch_data->size); } diff --git a/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp b/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp index 1d3b46b43..7626b9c9f 100644 --- a/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp +++ b/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp @@ -39,7 +39,7 @@ static void DumpSrtProgram(const Shader::Info& info, const u8* code, size_t code std::filesystem::create_directories(dump_dir); } const auto filename = fmt::format("{}_{:#018x}.srtprogram.txt", info.stage, info.pgm_hash); - const auto file = IOFile{dump_dir / filename, FileAccessMode::Write, FileType::TextFile}; + const auto file = IOFile{dump_dir / filename, FileAccessMode::Create, FileType::TextFile}; u64 address = reinterpret_cast(code); u64 code_end = address + codesize; diff --git a/src/shader_recompiler/ir/program.cpp b/src/shader_recompiler/ir/program.cpp index f2f6e34fa..1d03ea9ab 100644 --- a/src/shader_recompiler/ir/program.cpp +++ b/src/shader_recompiler/ir/program.cpp @@ -28,7 +28,7 @@ void DumpProgram(const Program& program, const Info& info, const std::string& ty } const auto ir_filename = fmt::format("{}_{:#018x}.{}irprogram.txt", info.stage, info.pgm_hash, type); - const auto ir_file = IOFile{dump_dir / ir_filename, FileAccessMode::Write, FileType::TextFile}; + const auto ir_file = IOFile{dump_dir / ir_filename, FileAccessMode::Create, FileType::TextFile}; size_t index{0}; std::map inst_to_index; @@ -46,7 +46,7 @@ void DumpProgram(const Program& program, const Info& info, const std::string& ty const auto asl_filename = fmt::format("{}_{:#018x}.{}asl.txt", info.stage, info.pgm_hash, type); const auto asl_file = - IOFile{dump_dir / asl_filename, FileAccessMode::Write, FileType::TextFile}; + IOFile{dump_dir / asl_filename, FileAccessMode::Create, FileType::TextFile}; for (const auto& node : program.syntax_list) { std::string s = IR::DumpASLNode(node, block_to_index, inst_to_index) + '\n'; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 24daf9c1c..be9543737 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -632,7 +632,7 @@ void PipelineCache::DumpShader(std::span code, u64 hash, Shader::Stag std::filesystem::create_directories(dump_dir); } const auto filename = fmt::format("{}.{}", GetShaderName(stage, hash, perm_idx), ext); - const auto file = IOFile{dump_dir / filename, FileAccessMode::Write}; + const auto file = IOFile{dump_dir / filename, FileAccessMode::Create}; file.WriteSpan(code); } From ff8869262f8450ef692bbc599058398ec3e9be7c Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Tue, 4 Nov 2025 17:14:22 +0200 Subject: [PATCH 03/25] [Libs] Font lib HLE implementation (#2761) * dummy fontlib libs * register font libs * typo fix * added font error file * added sceFontCharacterGetBidiLevel (from RE) * fixup * sceFontCharacterGetTextOrder , sceFontCharacterLooksFormatCharacters , sceFontCharacterLooksWhiteSpace RE * sceFontCharacterRefersTextBack,sceFontCharacterRefersTextNext,sceFontRenderSurfaceInit,sceFontRenderSurfaceSetScissor RE * sceFontRenderSurfaceSetStyleFrame ,sceFontStyleFrameGetEffectSlant RE added * clang fix * sceFontStyleFrameGetEffectWeight * added sceFontStyleFrameGetScalePixel * Update types.h * fixup merge --- CMakeLists.txt | 6 + src/common/logging/filter.cpp | 2 + src/common/logging/types.h | 2 + src/core/libraries/font/font.cpp | 1611 ++++++++++++++++++++++++++ src/core/libraries/font/font.h | 299 +++++ src/core/libraries/font/font_error.h | 44 + src/core/libraries/font/fontft.cpp | 140 +++ src/core/libraries/font/fontft.h | 37 + src/emulator.cpp | 6 +- 9 files changed, 2145 insertions(+), 2 deletions(-) create mode 100644 src/core/libraries/font/font.cpp create mode 100644 src/core/libraries/font/font.h create mode 100644 src/core/libraries/font/font_error.h create mode 100644 src/core/libraries/font/fontft.cpp create mode 100644 src/core/libraries/font/fontft.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f6144bc5..b75da04d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -461,6 +461,12 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/mouse/mouse.h src/core/libraries/web_browser_dialog/webbrowserdialog.cpp src/core/libraries/web_browser_dialog/webbrowserdialog.h + src/core/libraries/font/font.cpp + src/core/libraries/font/font.h + src/core/libraries/font/fontft.cpp + src/core/libraries/font/fontft.h + src/core/libraries/font/font_error.h + ) set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 2f98f07a0..bf6844c7d 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -140,6 +140,8 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, NpParty) \ SUB(Lib, Zlib) \ SUB(Lib, Hmd) \ + SUB(Lib, Font) \ + SUB(Lib, FontFt) \ SUB(Lib, HmdSetupDialog) \ SUB(Lib, SigninDialog) \ SUB(Lib, Camera) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index eee9d4ca2..035a959db 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -114,6 +114,8 @@ enum class Class : u8 { Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation. Lib_CompanionUtil, ///< The LibCompanionUtil implementation. Lib_VrTracker, ///< The LibSceVrTracker implementation. + Lib_Font, ///< The libSceFont implementation. + Lib_FontFt, ///< The libSceFontFt implementation. Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp new file mode 100644 index 000000000..1454004aa --- /dev/null +++ b/src/core/libraries/font/font.cpp @@ -0,0 +1,1611 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/font/font.h" +#include "core/libraries/libs.h" +#include "font_error.h" + +namespace Libraries::Font { + +s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontBindRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel(OrbisFontTextCharacter* textCharacter, + int* bidiLevel) { + if (!textCharacter || !bidiLevel) { + LOG_DEBUG(Lib_Font, "Invalid parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + *bidiLevel = textCharacter->bidiLevel; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterGetSyllableStringState() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterGetTextFontCode() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder(OrbisFontTextCharacter* textCharacter, + void** pTextOrder) { + if (!pTextOrder) { + LOG_DEBUG(Lib_Font, "Invalid parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + if (!textCharacter) { + LOG_DEBUG(Lib_Font, "Invalid parameter"); + *pTextOrder = NULL; + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Retrieve text order + *pTextOrder = textCharacter->textOrder; + return ORBIS_OK; +} + +u32 PS4_SYSV_ABI sceFontCharacterLooksFormatCharacters(OrbisFontTextCharacter* textCharacter) { + if (!textCharacter) { + return 0; + } + + // Check if the format flag (bit 2) is set + return (textCharacter->formatFlags & 0x04) ? textCharacter->characterCode : 0; +} + +u32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace(OrbisFontTextCharacter* textCharacter) { + if (!textCharacter) { + return 0; + } + + return (textCharacter->charType == 0x0E) ? textCharacter->characterCode : 0; +} + +OrbisFontTextCharacter* PS4_SYSV_ABI +sceFontCharacterRefersTextBack(OrbisFontTextCharacter* textCharacter) { + if (!textCharacter) + return NULL; // Check if input is NULL + + OrbisFontTextCharacter* current = textCharacter->prev; // Move backward instead of forward + while (current) { + if (current->unkn_0x31 == 0 && current->unkn_0x33 == 0) { + return current; // Return the first matching node + } + current = current->prev; // Move to the previous node + } + + return NULL; // No valid node found +} + +OrbisFontTextCharacter* PS4_SYSV_ABI +sceFontCharacterRefersTextNext(OrbisFontTextCharacter* textCharacter) { + if (!textCharacter) + return NULL; // Null check + + OrbisFontTextCharacter* current = textCharacter->next; + while (current) { + if (current->unkn_0x31 == 0 && current->unkn_0x33 == 0) { + return current; // Found a match + } + current = current->next; // Move to the next node + } + + return NULL; // No matching node found +} + +s32 PS4_SYSV_ABI sceFontCharactersRefersTextCodes() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontClearDeviceCache() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCloseFont() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontControl() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateGraphicsDevice() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateGraphicsService() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateGraphicsServiceWithEdition() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateLibrary() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateString() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateWords() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateWritingLine() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDefineAttribute() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDeleteGlyph() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyGraphicsDevice() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyGraphicsService() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyLibrary() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyString() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyWords() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyWritingLine() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDettachDeviceCacheBuffer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGenerateCharGlyph() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetAttribute() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetCharGlyphCode() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontGlyphsCount() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontGlyphsOutlineProfile() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontResolution() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontStyleInformation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetHorizontalLayout() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetKerning() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetLibrary() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetPixelResolution() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetResolutionDpi() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetScriptLanguage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetTypographicDesign() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetVerticalLayout() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphDefineAttribute() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphGetAttribute() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphGetGlyphForm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphGetMetricsForm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphGetScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontal() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalAdvance() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalX() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersOutline() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRenderImage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRenderImageHorizontal() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRenderImageVertical() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsBeginFrame() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsDrawingCancel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsDrawingFinish() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsEndFrame() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsExchangeResource() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillMethodInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetLayout() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetMapping() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetFillEffect() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetLayout() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetMapping() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsGetDeviceUsage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRegionInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRegionInitCircular() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRegionInitRoundish() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRelease() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRenderResource() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetFramePolicy() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupClipping() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupColorRates() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupFillMethod() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupFillRates() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFill() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFillPlot() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupHandleDefault() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupLocation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupPositioning() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupRotation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupScaling() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFill() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFillPlot() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvas() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvasSequence() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureDesign() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureDesignResource() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureSurfaceTexture() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateClipping() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateColorRates() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillMethod() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillRates() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFill() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFillPlot() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateLocation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdatePositioning() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateRotation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateScaling() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFill() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFillPlot() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontMemoryInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontMemoryTerm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontOpenFontFile() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontOpenFontInstance() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontOpenFontMemory() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontOpenFontSet() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRebindRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface, void* buffer, + int bufWidthByte, int pixelSizeByte, int widthPixel, + int heightPixel) { + if (renderSurface) { // Ensure surface is not NULL before modifying it + renderSurface->buffer = buffer; + renderSurface->widthByte = bufWidthByte; + renderSurface->pixelSizeByte = pixelSizeByte; + + // Initialize unknown fields (likely reserved or flags) + renderSurface->unkn_0xd = 0; + renderSurface->styleFlag = 0; + renderSurface->unkn_0xf = 0; + + // Ensure width and height are non-negative + renderSurface->width = (widthPixel < 0) ? 0 : widthPixel; + renderSurface->height = (heightPixel < 0) ? 0 : heightPixel; + + // Set the clipping/scaling rectangle + renderSurface->sc_x0 = 0; + renderSurface->sc_y0 = 0; + renderSurface->sc_x1 = renderSurface->width; + renderSurface->sc_y1 = renderSurface->height; + } +} + +void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderSurface, int x0, + int y0, int w, int h) { + if (!renderSurface) + return; // Null check + + // Handle horizontal clipping + int surfaceWidth = renderSurface->width; + int clip_x0, clip_x1; + + if (surfaceWidth != 0) { + if (x0 < 0) { // Adjust for negative x0 + clip_x0 = 0; + clip_x1 = (w + x0 > surfaceWidth) ? surfaceWidth : w + x0; + if (w <= -x0) + clip_x1 = 0; // Entire width is clipped + } else { + clip_x0 = (x0 > surfaceWidth) ? surfaceWidth : x0; + clip_x1 = (w + x0 > surfaceWidth) ? surfaceWidth : w + x0; + } + renderSurface->sc_x0 = clip_x0; + renderSurface->sc_x1 = clip_x1; + } + + // Handle vertical clipping + int surfaceHeight = renderSurface->height; + int clip_y0, clip_y1; + + if (surfaceHeight != 0) { + if (y0 < 0) { // Adjust for negative y0 + clip_y0 = 0; + clip_y1 = (h + y0 > surfaceHeight) ? surfaceHeight : h + y0; + if (h <= -y0) + clip_y1 = 0; // Entire height is clipped + } else { + clip_y0 = (y0 > surfaceHeight) ? surfaceHeight : y0; + clip_y1 = (h + y0 > surfaceHeight) ? surfaceHeight : h + y0; + } + renderSurface->sc_y0 = clip_y0; + renderSurface->sc_y1 = clip_y1; + } +} + +s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* renderSurface, + OrbisFontStyleFrame* styleFrame) { + if (!renderSurface) { + LOG_ERROR(Lib_Font, "Invalid Parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + if (!styleFrame) { + renderSurface->styleFlag &= 0xFE; // Clear style flag + } else { + // Validate magic number + if (styleFrame->magic != 0xF09) { + LOG_ERROR(Lib_Font, "Invalid magic"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + renderSurface->styleFlag |= 1; // Set style flag + } + + // Assign style frame pointer + renderSurface->unkn_28[0] = styleFrame; + *(uint32_t*)(renderSurface->unkn_28 + 1) = 0; // Reset related field + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetFontsOpenMode() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetResolutionDpi() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetScriptLanguage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetTypographicDesign() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringGetTerminateCode() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringGetTerminateOrder() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringGetWritingForm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringRefersRenderCharacters() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringRefersTextCharacters() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(OrbisFontStyleFrame* styleFrame, + float* slantRatio) { + if (!styleFrame) { + LOG_ERROR(Lib_Font, "Invalid Parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Validate the magic number + if (styleFrame->magic != 0xF09) { + LOG_ERROR(Lib_Font, "Invalid Magic"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Check if the slant effect is enabled (bit 1 in flags) + if (!(styleFrame->flags & 0x02)) { + LOG_ERROR(Lib_Font, "Flag not set"); + return ORBIS_FONT_ERROR_UNSET_PARAMETER; + } + + if (!slantRatio) { + LOG_ERROR(Lib_Font, "Invalid Parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Retrieve slant ratio + *slantRatio = styleFrame->slantRatio; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(OrbisFontStyleFrame* fontStyleFrame, + float* weightXScale, float* weightYScale, + uint32_t* mode) { + if (!fontStyleFrame) { + LOG_ERROR(Lib_Font, "Invalid Parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Validate the magic number + if (fontStyleFrame->magic != 0xF09) { + LOG_ERROR(Lib_Font, "Magic not set"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Check if the weight effect is enabled (bit 2 in flags) + if (!(fontStyleFrame->flags & 0x04)) { + LOG_ERROR(Lib_Font, "Flag not set"); + return ORBIS_FONT_ERROR_UNSET_PARAMETER; + } + + // Retrieve weight scales (default is +1.0 to maintain normal weight) + if (weightXScale) { + *weightXScale = fontStyleFrame->weightXScale + 1.0f; + } + if (weightYScale) { + *weightYScale = fontStyleFrame->weightYScale + 1.0f; + } + + // Reset mode if provided + if (mode) { + *mode = 0; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(OrbisFontStyleFrame* styleFrame, float* w, + float* h) { + if (!styleFrame) { + LOG_ERROR(Lib_Font, "Invalid Parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + if (styleFrame->magic != 0xF09) { + LOG_ERROR(Lib_Font, "Invalid magic"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + if (!(styleFrame->flags & 0x01)) { + LOG_ERROR(Lib_Font, "Scaling effect parameter not set"); + return ORBIS_FONT_ERROR_UNSET_PARAMETER; + } + + // Check if scaling is allowed + int isScalingEnabled = styleFrame->scalingFlag; + if (w) { + *w = styleFrame->scaleWidth; + if (isScalingEnabled && styleFrame->dpiX) { + *w *= ((float)styleFrame->dpiX / 72.0f); + } + } + + if (h) { + *h = styleFrame->scaleHeight; + if (isScalingEnabled && styleFrame->dpiY) { + *h *= ((float)styleFrame->dpiY / 72.0f); + } + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetResolutionDpi() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSupportExternalFonts() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSupportGlyphs() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSupportSystemFonts() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextCodesStepBack() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextCodesStepNext() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextSourceInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextSourceRewind() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextSourceSetDefaultFont() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextSourceSetWritingForm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontUnbindRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWordsFindWordCharacters() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingGetRenderMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineClear() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineGetOrderingSpace() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineGetRenderMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineRefersRenderStep() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineWritesOrder() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingRefersRenderStep() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingRefersRenderStepCharacter() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingSetMaskInvisible() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_00F4D778F1C88CB3() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_03C650025FBB0DE7() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_07EAB8A163B27E1A() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_09408E88E4F97CE3() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_09F92905ED82A814() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_0D142CEE1AB21ABE() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_14BD2E9E119C16F2() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_1AC53C9EDEAE8D75() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_1D401185D5E24C3D() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_1E83CD20C2CC996F() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_314B1F765B9FE78A() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_350E6725FEDE29E1() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_3DB773F0A604BF39() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_4FF49DD21E311B1C() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_526287664A493981() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_55CA718DBC84A6E9() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_563FC5F0706A8B4D() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_569E2ECD34290F45() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_5A04775B6BE47685() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_5FD93BCAB6F79750() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_62B5398F864BD3B4() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_6F9010294D822367() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_7757E947423A7A67() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_7E06BA52077F54FA() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_93B36DEA021311D6() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_94B0891E7111598A() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_9785C9128C2FE7CD() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_97DFBC9B65FBC0E1() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_ACD9717405D7D3CA() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_B19A8AEC3FD4F16F() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_C10F488AD7CF103D() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_D0C8B5FF4A6826C7() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_E48D3CD01C342A33() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_EAC96B2186B71E14() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_FE4788A96EF46256() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_FE7E5AE95D3058F5() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("CUKn5pX-NVY", "libSceFont", 1, "libSceFont", sceFontAttachDeviceCacheBuffer); + LIB_FUNCTION("3OdRkSjOcog", "libSceFont", 1, "libSceFont", sceFontBindRenderer); + LIB_FUNCTION("6DFUkCwQLa8", "libSceFont", 1, "libSceFont", sceFontCharacterGetBidiLevel); + LIB_FUNCTION("coCrV6IWplE", "libSceFont", 1, "libSceFont", + sceFontCharacterGetSyllableStringState); + LIB_FUNCTION("zN3+nuA0SFQ", "libSceFont", 1, "libSceFont", sceFontCharacterGetTextFontCode); + LIB_FUNCTION("mxgmMj-Mq-o", "libSceFont", 1, "libSceFont", sceFontCharacterGetTextOrder); + LIB_FUNCTION("-P6X35Rq2-E", "libSceFont", 1, "libSceFont", + sceFontCharacterLooksFormatCharacters); + LIB_FUNCTION("SaRlqtqaCew", "libSceFont", 1, "libSceFont", sceFontCharacterLooksWhiteSpace); + LIB_FUNCTION("6Gqlv5KdTbU", "libSceFont", 1, "libSceFont", sceFontCharacterRefersTextBack); + LIB_FUNCTION("BkjBP+YC19w", "libSceFont", 1, "libSceFont", sceFontCharacterRefersTextNext); + LIB_FUNCTION("lVSR5ftvNag", "libSceFont", 1, "libSceFont", sceFontCharactersRefersTextCodes); + LIB_FUNCTION("I9R5VC6eZWo", "libSceFont", 1, "libSceFont", sceFontClearDeviceCache); + LIB_FUNCTION("vzHs3C8lWJk", "libSceFont", 1, "libSceFont", sceFontCloseFont); + LIB_FUNCTION("MpKSBaYKluo", "libSceFont", 1, "libSceFont", sceFontControl); + LIB_FUNCTION("WBNBaj9XiJU", "libSceFont", 1, "libSceFont", sceFontCreateGraphicsDevice); + LIB_FUNCTION("4So0MC3oBIM", "libSceFont", 1, "libSceFont", sceFontCreateGraphicsService); + LIB_FUNCTION("NlO5Qlhjkng", "libSceFont", 1, "libSceFont", + sceFontCreateGraphicsServiceWithEdition); + LIB_FUNCTION("nWrfPI4Okmg", "libSceFont", 1, "libSceFont", sceFontCreateLibrary); + LIB_FUNCTION("n590hj5Oe-k", "libSceFont", 1, "libSceFont", sceFontCreateLibraryWithEdition); + LIB_FUNCTION("u5fZd3KZcs0", "libSceFont", 1, "libSceFont", sceFontCreateRenderer); + LIB_FUNCTION("WaSFJoRWXaI", "libSceFont", 1, "libSceFont", sceFontCreateRendererWithEdition); + LIB_FUNCTION("MO24vDhmS4E", "libSceFont", 1, "libSceFont", sceFontCreateString); + LIB_FUNCTION("cYrMGk1wrMA", "libSceFont", 1, "libSceFont", sceFontCreateWords); + LIB_FUNCTION("7rogx92EEyc", "libSceFont", 1, "libSceFont", sceFontCreateWritingLine); + LIB_FUNCTION("8h-SOB-asgk", "libSceFont", 1, "libSceFont", sceFontDefineAttribute); + LIB_FUNCTION("LHDoRWVFGqk", "libSceFont", 1, "libSceFont", sceFontDeleteGlyph); + LIB_FUNCTION("5QG71IjgOpQ", "libSceFont", 1, "libSceFont", sceFontDestroyGraphicsDevice); + LIB_FUNCTION("zZQD3EwJo3c", "libSceFont", 1, "libSceFont", sceFontDestroyGraphicsService); + LIB_FUNCTION("FXP359ygujs", "libSceFont", 1, "libSceFont", sceFontDestroyLibrary); + LIB_FUNCTION("exAxkyVLt0s", "libSceFont", 1, "libSceFont", sceFontDestroyRenderer); + LIB_FUNCTION("SSCaczu2aMQ", "libSceFont", 1, "libSceFont", sceFontDestroyString); + LIB_FUNCTION("hWE4AwNixqY", "libSceFont", 1, "libSceFont", sceFontDestroyWords); + LIB_FUNCTION("PEjv7CVDRYs", "libSceFont", 1, "libSceFont", sceFontDestroyWritingLine); + LIB_FUNCTION("UuY-OJF+f0k", "libSceFont", 1, "libSceFont", sceFontDettachDeviceCacheBuffer); + LIB_FUNCTION("C-4Qw5Srlyw", "libSceFont", 1, "libSceFont", sceFontGenerateCharGlyph); + LIB_FUNCTION("5kx49CAlO-M", "libSceFont", 1, "libSceFont", sceFontGetAttribute); + LIB_FUNCTION("OINC0X9HGBY", "libSceFont", 1, "libSceFont", sceFontGetCharGlyphCode); + LIB_FUNCTION("L97d+3OgMlE", "libSceFont", 1, "libSceFont", sceFontGetCharGlyphMetrics); + LIB_FUNCTION("ynSqYL8VpoA", "libSceFont", 1, "libSceFont", sceFontGetEffectSlant); + LIB_FUNCTION("d7dDgRY+Bzw", "libSceFont", 1, "libSceFont", sceFontGetEffectWeight); + LIB_FUNCTION("ZB8xRemRRG8", "libSceFont", 1, "libSceFont", sceFontGetFontGlyphsCount); + LIB_FUNCTION("4X14YSK4Ldk", "libSceFont", 1, "libSceFont", sceFontGetFontGlyphsOutlineProfile); + LIB_FUNCTION("eb9S3zNlV5o", "libSceFont", 1, "libSceFont", sceFontGetFontMetrics); + LIB_FUNCTION("tiIlroGki+g", "libSceFont", 1, "libSceFont", sceFontGetFontResolution); + LIB_FUNCTION("3hVv3SNoL6E", "libSceFont", 1, "libSceFont", sceFontGetFontStyleInformation); + LIB_FUNCTION("gVQpMBuB7fE", "libSceFont", 1, "libSceFont", sceFontGetGlyphExpandBufferState); + LIB_FUNCTION("imxVx8lm+KM", "libSceFont", 1, "libSceFont", sceFontGetHorizontalLayout); + LIB_FUNCTION("sDuhHGNhHvE", "libSceFont", 1, "libSceFont", sceFontGetKerning); + LIB_FUNCTION("LzmHDnlcwfQ", "libSceFont", 1, "libSceFont", sceFontGetLibrary); + LIB_FUNCTION("BozJej5T6fs", "libSceFont", 1, "libSceFont", sceFontGetPixelResolution); + LIB_FUNCTION("IQtleGLL5pQ", "libSceFont", 1, "libSceFont", sceFontGetRenderCharGlyphMetrics); + LIB_FUNCTION("Gqa5Pp7y4MU", "libSceFont", 1, "libSceFont", sceFontGetRenderEffectSlant); + LIB_FUNCTION("woOjHrkjIYg", "libSceFont", 1, "libSceFont", sceFontGetRenderEffectWeight); + LIB_FUNCTION("ryPlnDDI3rU", "libSceFont", 1, "libSceFont", sceFontGetRenderScaledKerning); + LIB_FUNCTION("EY38A01lq2k", "libSceFont", 1, "libSceFont", sceFontGetRenderScalePixel); + LIB_FUNCTION("FEafYUcxEGo", "libSceFont", 1, "libSceFont", sceFontGetRenderScalePoint); + LIB_FUNCTION("8REoLjNGCpM", "libSceFont", 1, "libSceFont", sceFontGetResolutionDpi); + LIB_FUNCTION("CkVmLoCNN-8", "libSceFont", 1, "libSceFont", sceFontGetScalePixel); + LIB_FUNCTION("GoF2bhB7LYk", "libSceFont", 1, "libSceFont", sceFontGetScalePoint); + LIB_FUNCTION("IrXeG0Lc6nA", "libSceFont", 1, "libSceFont", sceFontGetScriptLanguage); + LIB_FUNCTION("7-miUT6pNQw", "libSceFont", 1, "libSceFont", sceFontGetTypographicDesign); + LIB_FUNCTION("3BrWWFU+4ts", "libSceFont", 1, "libSceFont", sceFontGetVerticalLayout); + LIB_FUNCTION("8-zmgsxkBek", "libSceFont", 1, "libSceFont", sceFontGlyphDefineAttribute); + LIB_FUNCTION("oO33Uex4Ui0", "libSceFont", 1, "libSceFont", sceFontGlyphGetAttribute); + LIB_FUNCTION("PXlA0M8ax40", "libSceFont", 1, "libSceFont", sceFontGlyphGetGlyphForm); + LIB_FUNCTION("XUfSWpLhrUw", "libSceFont", 1, "libSceFont", sceFontGlyphGetMetricsForm); + LIB_FUNCTION("lNnUqa1zA-M", "libSceFont", 1, "libSceFont", sceFontGlyphGetScalePixel); + LIB_FUNCTION("ntrc3bEWlvQ", "libSceFont", 1, "libSceFont", sceFontGlyphRefersMetrics); + LIB_FUNCTION("9kTbF59TjLs", "libSceFont", 1, "libSceFont", sceFontGlyphRefersMetricsHorizontal); + LIB_FUNCTION("nJavPEdMDvM", "libSceFont", 1, "libSceFont", + sceFontGlyphRefersMetricsHorizontalAdvance); + LIB_FUNCTION("JCnVgZgcucs", "libSceFont", 1, "libSceFont", + sceFontGlyphRefersMetricsHorizontalX); + LIB_FUNCTION("R1T4i+DOhNY", "libSceFont", 1, "libSceFont", sceFontGlyphRefersOutline); + LIB_FUNCTION("RmkXfBcZnrM", "libSceFont", 1, "libSceFont", sceFontGlyphRenderImage); + LIB_FUNCTION("r4KEihtwxGs", "libSceFont", 1, "libSceFont", sceFontGlyphRenderImageHorizontal); + LIB_FUNCTION("n22d-HIdmMg", "libSceFont", 1, "libSceFont", sceFontGlyphRenderImageVertical); + LIB_FUNCTION("RL2cAQgyXR8", "libSceFont", 1, "libSceFont", sceFontGraphicsBeginFrame); + LIB_FUNCTION("dUmIK6QjT7E", "libSceFont", 1, "libSceFont", sceFontGraphicsDrawingCancel); + LIB_FUNCTION("X2Vl3yU19Zw", "libSceFont", 1, "libSceFont", sceFontGraphicsDrawingFinish); + LIB_FUNCTION("DOmdOwV3Aqw", "libSceFont", 1, "libSceFont", sceFontGraphicsEndFrame); + LIB_FUNCTION("zdYdKRQC3rw", "libSceFont", 1, "libSceFont", sceFontGraphicsExchangeResource); + LIB_FUNCTION("UkMUIoj-e9s", "libSceFont", 1, "libSceFont", sceFontGraphicsFillMethodInit); + LIB_FUNCTION("DJURdcnVUqo", "libSceFont", 1, "libSceFont", sceFontGraphicsFillPlotInit); + LIB_FUNCTION("eQac6ftmBQQ", "libSceFont", 1, "libSceFont", sceFontGraphicsFillPlotSetLayout); + LIB_FUNCTION("PEYQJa+MWnk", "libSceFont", 1, "libSceFont", sceFontGraphicsFillPlotSetMapping); + LIB_FUNCTION("21g4m4kYF6g", "libSceFont", 1, "libSceFont", sceFontGraphicsFillRatesInit); + LIB_FUNCTION("pJzji5FvdxU", "libSceFont", 1, "libSceFont", + sceFontGraphicsFillRatesSetFillEffect); + LIB_FUNCTION("scaro-xEuUM", "libSceFont", 1, "libSceFont", sceFontGraphicsFillRatesSetLayout); + LIB_FUNCTION("W66Kqtt0xU0", "libSceFont", 1, "libSceFont", sceFontGraphicsFillRatesSetMapping); + LIB_FUNCTION("FzpLsBQEegQ", "libSceFont", 1, "libSceFont", sceFontGraphicsGetDeviceUsage); + LIB_FUNCTION("W80hs0g5d+E", "libSceFont", 1, "libSceFont", sceFontGraphicsRegionInit); + LIB_FUNCTION("S48+njg9p-o", "libSceFont", 1, "libSceFont", sceFontGraphicsRegionInitCircular); + LIB_FUNCTION("wcOQ8Fz73+M", "libSceFont", 1, "libSceFont", sceFontGraphicsRegionInitRoundish); + LIB_FUNCTION("YBaw2Yyfd5E", "libSceFont", 1, "libSceFont", sceFontGraphicsRelease); + LIB_FUNCTION("qkySrQ4FGe0", "libSceFont", 1, "libSceFont", sceFontGraphicsRenderResource); + LIB_FUNCTION("qzNjJYKVli0", "libSceFont", 1, "libSceFont", sceFontGraphicsSetFramePolicy); + LIB_FUNCTION("9iRbHCtcx-o", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupClipping); + LIB_FUNCTION("KZ3qPyz5Opc", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupColorRates); + LIB_FUNCTION("LqclbpVzRvM", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupFillMethod); + LIB_FUNCTION("Wl4FiI4qKY0", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupFillRates); + LIB_FUNCTION("WC7s95TccVo", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupGlyphFill); + LIB_FUNCTION("zC6I4ty37NA", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupGlyphFillPlot); + LIB_FUNCTION("drZUF0XKTEI", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupHandleDefault); + LIB_FUNCTION("MEAmHMynQXE", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupLocation); + LIB_FUNCTION("XRUOmQhnYO4", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupPositioning); + LIB_FUNCTION("98XGr2Bkklg", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupRotation); + LIB_FUNCTION("Nj-ZUVOVAvc", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupScaling); + LIB_FUNCTION("p0avT2ggev0", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupShapeFill); + LIB_FUNCTION("0C5aKg9KghY", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupShapeFillPlot); + LIB_FUNCTION("4pA3qqAcYco", "libSceFont", 1, "libSceFont", sceFontGraphicsStructureCanvas); + LIB_FUNCTION("cpjgdlMYdOM", "libSceFont", 1, "libSceFont", + sceFontGraphicsStructureCanvasSequence); + LIB_FUNCTION("774Mee21wKk", "libSceFont", 1, "libSceFont", sceFontGraphicsStructureDesign); + LIB_FUNCTION("Hp3NIFhUXvQ", "libSceFont", 1, "libSceFont", + sceFontGraphicsStructureDesignResource); + LIB_FUNCTION("bhmZlml6NBs", "libSceFont", 1, "libSceFont", + sceFontGraphicsStructureSurfaceTexture); + LIB_FUNCTION("5sAWgysOBfE", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateClipping); + LIB_FUNCTION("W4e8obm+w6o", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateColorRates); + LIB_FUNCTION("EgIn3QBajPs", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateFillMethod); + LIB_FUNCTION("MnUYAs2jVuU", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateFillRates); + LIB_FUNCTION("R-oVDMusYbc", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateGlyphFill); + LIB_FUNCTION("b9R+HQuHSMI", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateGlyphFillPlot); + LIB_FUNCTION("IN4P5pJADQY", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateLocation); + LIB_FUNCTION("U+LLXdr2DxM", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdatePositioning); + LIB_FUNCTION("yStTYSeb4NM", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateRotation); + LIB_FUNCTION("eDxmMoxE5xU", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateScaling); + LIB_FUNCTION("Ax6LQJJq6HQ", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateShapeFill); + LIB_FUNCTION("I5Rf2rXvBKQ", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateShapeFillPlot); + LIB_FUNCTION("whrS4oksXc4", "libSceFont", 1, "libSceFont", sceFontMemoryInit); + LIB_FUNCTION("h6hIgxXEiEc", "libSceFont", 1, "libSceFont", sceFontMemoryTerm); + LIB_FUNCTION("RvXyHMUiLhE", "libSceFont", 1, "libSceFont", sceFontOpenFontFile); + LIB_FUNCTION("JzCH3SCFnAU", "libSceFont", 1, "libSceFont", sceFontOpenFontInstance); + LIB_FUNCTION("KXUpebrFk1U", "libSceFont", 1, "libSceFont", sceFontOpenFontMemory); + LIB_FUNCTION("cKYtVmeSTcw", "libSceFont", 1, "libSceFont", sceFontOpenFontSet); + LIB_FUNCTION("Z2cdsqJH+5k", "libSceFont", 1, "libSceFont", sceFontRebindRenderer); + LIB_FUNCTION("3G4zhgKuxE8", "libSceFont", 1, "libSceFont", sceFontRenderCharGlyphImage); + LIB_FUNCTION("kAenWy1Zw5o", "libSceFont", 1, "libSceFont", + sceFontRenderCharGlyphImageHorizontal); + LIB_FUNCTION("i6UNdSig1uE", "libSceFont", 1, "libSceFont", sceFontRenderCharGlyphImageVertical); + LIB_FUNCTION("amcmrY62BD4", "libSceFont", 1, "libSceFont", sceFontRendererGetOutlineBufferSize); + LIB_FUNCTION("ai6AfGrBs4o", "libSceFont", 1, "libSceFont", sceFontRendererResetOutlineBuffer); + LIB_FUNCTION("ydF+WuH0fAk", "libSceFont", 1, "libSceFont", + sceFontRendererSetOutlineBufferPolicy); + LIB_FUNCTION("gdUCnU0gHdI", "libSceFont", 1, "libSceFont", sceFontRenderSurfaceInit); + LIB_FUNCTION("vRxf4d0ulPs", "libSceFont", 1, "libSceFont", sceFontRenderSurfaceSetScissor); + LIB_FUNCTION("0hr-w30SjiI", "libSceFont", 1, "libSceFont", sceFontRenderSurfaceSetStyleFrame); + LIB_FUNCTION("TMtqoFQjjbA", "libSceFont", 1, "libSceFont", sceFontSetEffectSlant); + LIB_FUNCTION("v0phZwa4R5o", "libSceFont", 1, "libSceFont", sceFontSetEffectWeight); + LIB_FUNCTION("kihFGYJee7o", "libSceFont", 1, "libSceFont", sceFontSetFontsOpenMode); + LIB_FUNCTION("I1acwR7Qp8E", "libSceFont", 1, "libSceFont", sceFontSetResolutionDpi); + LIB_FUNCTION("N1EBMeGhf7E", "libSceFont", 1, "libSceFont", sceFontSetScalePixel); + LIB_FUNCTION("sw65+7wXCKE", "libSceFont", 1, "libSceFont", sceFontSetScalePoint); + LIB_FUNCTION("PxSR9UfJ+SQ", "libSceFont", 1, "libSceFont", sceFontSetScriptLanguage); + LIB_FUNCTION("SnsZua35ngs", "libSceFont", 1, "libSceFont", sceFontSetTypographicDesign); + LIB_FUNCTION("lz9y9UFO2UU", "libSceFont", 1, "libSceFont", sceFontSetupRenderEffectSlant); + LIB_FUNCTION("XIGorvLusDQ", "libSceFont", 1, "libSceFont", sceFontSetupRenderEffectWeight); + LIB_FUNCTION("6vGCkkQJOcI", "libSceFont", 1, "libSceFont", sceFontSetupRenderScalePixel); + LIB_FUNCTION("nMZid4oDfi4", "libSceFont", 1, "libSceFont", sceFontSetupRenderScalePoint); + LIB_FUNCTION("ObkDGDBsVtw", "libSceFont", 1, "libSceFont", sceFontStringGetTerminateCode); + LIB_FUNCTION("+B-xlbiWDJ4", "libSceFont", 1, "libSceFont", sceFontStringGetTerminateOrder); + LIB_FUNCTION("o1vIEHeb6tw", "libSceFont", 1, "libSceFont", sceFontStringGetWritingForm); + LIB_FUNCTION("hq5LffQjz-s", "libSceFont", 1, "libSceFont", sceFontStringRefersRenderCharacters); + LIB_FUNCTION("Avv7OApgCJk", "libSceFont", 1, "libSceFont", sceFontStringRefersTextCharacters); + LIB_FUNCTION("lOfduYnjgbo", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetEffectSlant); + LIB_FUNCTION("HIUdjR-+Wl8", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetEffectWeight); + LIB_FUNCTION("VSw18Aqzl0U", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetResolutionDpi); + LIB_FUNCTION("2QfqfeLblbg", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetScalePixel); + LIB_FUNCTION("7x2xKiiB7MA", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetScalePoint); + LIB_FUNCTION("la2AOWnHEAc", "libSceFont", 1, "libSceFont", sceFontStyleFrameInit); + LIB_FUNCTION("394sckksiCU", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetEffectSlant); + LIB_FUNCTION("faw77-pEBmU", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetEffectWeight); + LIB_FUNCTION("dB4-3Wdwls8", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetResolutionDpi); + LIB_FUNCTION("da4rQ4-+p-4", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetScalePixel); + LIB_FUNCTION("O997laxY-Ys", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetScalePoint); + LIB_FUNCTION("dUmABkAnVgk", "libSceFont", 1, "libSceFont", sceFontStyleFrameUnsetEffectSlant); + LIB_FUNCTION("hwsuXgmKdaw", "libSceFont", 1, "libSceFont", sceFontStyleFrameUnsetEffectWeight); + LIB_FUNCTION("bePC0L0vQWY", "libSceFont", 1, "libSceFont", sceFontStyleFrameUnsetScale); + LIB_FUNCTION("mz2iTY0MK4A", "libSceFont", 1, "libSceFont", sceFontSupportExternalFonts); + LIB_FUNCTION("71w5DzObuZI", "libSceFont", 1, "libSceFont", sceFontSupportGlyphs); + LIB_FUNCTION("SsRbbCiWoGw", "libSceFont", 1, "libSceFont", sceFontSupportSystemFonts); + LIB_FUNCTION("IPoYwwlMx-g", "libSceFont", 1, "libSceFont", sceFontTextCodesStepBack); + LIB_FUNCTION("olSmXY+XP1E", "libSceFont", 1, "libSceFont", sceFontTextCodesStepNext); + LIB_FUNCTION("oaJ1BpN2FQk", "libSceFont", 1, "libSceFont", sceFontTextSourceInit); + LIB_FUNCTION("VRFd3diReec", "libSceFont", 1, "libSceFont", sceFontTextSourceRewind); + LIB_FUNCTION("eCRMCSk96NU", "libSceFont", 1, "libSceFont", sceFontTextSourceSetDefaultFont); + LIB_FUNCTION("OqQKX0h5COw", "libSceFont", 1, "libSceFont", sceFontTextSourceSetWritingForm); + LIB_FUNCTION("1QjhKxrsOB8", "libSceFont", 1, "libSceFont", sceFontUnbindRenderer); + LIB_FUNCTION("H-FNq8isKE0", "libSceFont", 1, "libSceFont", sceFontWordsFindWordCharacters); + LIB_FUNCTION("fljdejMcG1c", "libSceFont", 1, "libSceFont", sceFontWritingGetRenderMetrics); + LIB_FUNCTION("fD5rqhEXKYQ", "libSceFont", 1, "libSceFont", sceFontWritingInit); + LIB_FUNCTION("1+DgKL0haWQ", "libSceFont", 1, "libSceFont", sceFontWritingLineClear); + LIB_FUNCTION("JQKWIsS9joE", "libSceFont", 1, "libSceFont", sceFontWritingLineGetOrderingSpace); + LIB_FUNCTION("nlU2VnfpqTM", "libSceFont", 1, "libSceFont", sceFontWritingLineGetRenderMetrics); + LIB_FUNCTION("+FYcYefsVX0", "libSceFont", 1, "libSceFont", sceFontWritingLineRefersRenderStep); + LIB_FUNCTION("wyKFUOWdu3Q", "libSceFont", 1, "libSceFont", sceFontWritingLineWritesOrder); + LIB_FUNCTION("W-2WOXEHGck", "libSceFont", 1, "libSceFont", sceFontWritingRefersRenderStep); + LIB_FUNCTION("f4Onl7efPEY", "libSceFont", 1, "libSceFont", + sceFontWritingRefersRenderStepCharacter); + LIB_FUNCTION("BbCZjJizU4A", "libSceFont", 1, "libSceFont", sceFontWritingSetMaskInvisible); + LIB_FUNCTION("APTXePHIjLM", "libSceFont", 1, "libSceFont", Func_00F4D778F1C88CB3); + LIB_FUNCTION("A8ZQAl+7Dec", "libSceFont", 1, "libSceFont", Func_03C650025FBB0DE7); + LIB_FUNCTION("B+q4oWOyfho", "libSceFont", 1, "libSceFont", Func_07EAB8A163B27E1A); + LIB_FUNCTION("CUCOiOT5fOM", "libSceFont", 1, "libSceFont", Func_09408E88E4F97CE3); + LIB_FUNCTION("CfkpBe2CqBQ", "libSceFont", 1, "libSceFont", Func_09F92905ED82A814); + LIB_FUNCTION("DRQs7hqyGr4", "libSceFont", 1, "libSceFont", Func_0D142CEE1AB21ABE); + LIB_FUNCTION("FL0unhGcFvI", "libSceFont", 1, "libSceFont", Func_14BD2E9E119C16F2); + LIB_FUNCTION("GsU8nt6ujXU", "libSceFont", 1, "libSceFont", Func_1AC53C9EDEAE8D75); + LIB_FUNCTION("HUARhdXiTD0", "libSceFont", 1, "libSceFont", Func_1D401185D5E24C3D); + LIB_FUNCTION("HoPNIMLMmW8", "libSceFont", 1, "libSceFont", Func_1E83CD20C2CC996F); + LIB_FUNCTION("MUsfdluf54o", "libSceFont", 1, "libSceFont", Func_314B1F765B9FE78A); + LIB_FUNCTION("NQ5nJf7eKeE", "libSceFont", 1, "libSceFont", Func_350E6725FEDE29E1); + LIB_FUNCTION("Pbdz8KYEvzk", "libSceFont", 1, "libSceFont", Func_3DB773F0A604BF39); + LIB_FUNCTION("T-Sd0h4xGxw", "libSceFont", 1, "libSceFont", Func_4FF49DD21E311B1C); + LIB_FUNCTION("UmKHZkpJOYE", "libSceFont", 1, "libSceFont", Func_526287664A493981); + LIB_FUNCTION("VcpxjbyEpuk", "libSceFont", 1, "libSceFont", Func_55CA718DBC84A6E9); + LIB_FUNCTION("Vj-F8HBqi00", "libSceFont", 1, "libSceFont", Func_563FC5F0706A8B4D); + LIB_FUNCTION("Vp4uzTQpD0U", "libSceFont", 1, "libSceFont", Func_569E2ECD34290F45); + LIB_FUNCTION("WgR3W2vkdoU", "libSceFont", 1, "libSceFont", Func_5A04775B6BE47685); + LIB_FUNCTION("X9k7yrb3l1A", "libSceFont", 1, "libSceFont", Func_5FD93BCAB6F79750); + LIB_FUNCTION("YrU5j4ZL07Q", "libSceFont", 1, "libSceFont", Func_62B5398F864BD3B4); + LIB_FUNCTION("b5AQKU2CI2c", "libSceFont", 1, "libSceFont", Func_6F9010294D822367); + LIB_FUNCTION("d1fpR0I6emc", "libSceFont", 1, "libSceFont", Func_7757E947423A7A67); + LIB_FUNCTION("fga6Ugd-VPo", "libSceFont", 1, "libSceFont", Func_7E06BA52077F54FA); + LIB_FUNCTION("k7Nt6gITEdY", "libSceFont", 1, "libSceFont", Func_93B36DEA021311D6); + LIB_FUNCTION("lLCJHnERWYo", "libSceFont", 1, "libSceFont", Func_94B0891E7111598A); + LIB_FUNCTION("l4XJEowv580", "libSceFont", 1, "libSceFont", Func_9785C9128C2FE7CD); + LIB_FUNCTION("l9+8m2X7wOE", "libSceFont", 1, "libSceFont", Func_97DFBC9B65FBC0E1); + LIB_FUNCTION("rNlxdAXX08o", "libSceFont", 1, "libSceFont", Func_ACD9717405D7D3CA); + LIB_FUNCTION("sZqK7D-U8W8", "libSceFont", 1, "libSceFont", Func_B19A8AEC3FD4F16F); + LIB_FUNCTION("wQ9IitfPED0", "libSceFont", 1, "libSceFont", Func_C10F488AD7CF103D); + LIB_FUNCTION("0Mi1-0poJsc", "libSceFont", 1, "libSceFont", Func_D0C8B5FF4A6826C7); + LIB_FUNCTION("5I080Bw0KjM", "libSceFont", 1, "libSceFont", Func_E48D3CD01C342A33); + LIB_FUNCTION("6slrIYa3HhQ", "libSceFont", 1, "libSceFont", Func_EAC96B2186B71E14); + LIB_FUNCTION("-keIqW70YlY", "libSceFont", 1, "libSceFont", Func_FE4788A96EF46256); + LIB_FUNCTION("-n5a6V0wWPU", "libSceFont", 1, "libSceFont", Func_FE7E5AE95D3058F5); +}; + +} // namespace Libraries::Font \ No newline at end of file diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h new file mode 100644 index 000000000..a8e239126 --- /dev/null +++ b/src/core/libraries/font/font.h @@ -0,0 +1,299 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Font { + +struct OrbisFontTextCharacter { + // Other fields... + struct OrbisFontTextCharacter* next; // Pointer to the next node 0x00 + struct OrbisFontTextCharacter* prev; // Pointer to the next node 0x08 + void* textOrder; // Field at offset 0x10 (pointer to text order info) + u32 characterCode; // Field assumed at offset 0x28 + u8 unkn_0x31; // Offset 0x31 + u8 unkn_0x33; // Offset 0x33 + u8 charType; // Field assumed at offset 0x39 + u8 bidiLevel; // Field assumed at offset 0x3B stores the Bidi level + u8 formatFlags; // Field at offset 0x3D (stores format-related flags) +}; + +struct OrbisFontRenderSurface { + void* buffer; + s32 widthByte; + s8 pixelSizeByte; + u8 unkn_0xd; + u8 styleFlag; + u8 unkn_0xf; + s32 width, height; + u32 sc_x0; + u32 sc_y0; + u32 sc_x1; + u32 sc_y1; + void* unkn_28[3]; +}; + +struct OrbisFontStyleFrame { + /*0x00*/ u16 magic; // Expected to be 0xF09 + /*0x02*/ u16 flags; + /*0x04*/ s32 dpiX; // DPI scaling factor for width + /*0x08*/ s32 dpiY; // DPI scaling factor for height + /*0x0c*/ s32 scalingFlag; // Indicates whether scaling is enabled + /*0x10*/ + /*0x14*/ float scaleWidth; // Width scaling factor + /*0x18*/ float scaleHeight; // Height scaling factor + /*0x1c*/ float weightXScale; + /*0x20*/ float weightYScale; + /*0x24*/ float slantRatio; +}; + +s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer(); +s32 PS4_SYSV_ABI sceFontBindRenderer(); +s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel(OrbisFontTextCharacter* textCharacter, + int* bidiLevel); +s32 PS4_SYSV_ABI sceFontCharacterGetSyllableStringState(); +s32 PS4_SYSV_ABI sceFontCharacterGetTextFontCode(); +s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder(OrbisFontTextCharacter* textCharacter, + void** pTextOrder); +u32 PS4_SYSV_ABI sceFontCharacterLooksFormatCharacters(OrbisFontTextCharacter* textCharacter); +u32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace(OrbisFontTextCharacter* textCharacter); +OrbisFontTextCharacter* PS4_SYSV_ABI +sceFontCharacterRefersTextBack(OrbisFontTextCharacter* textCharacter); +OrbisFontTextCharacter* PS4_SYSV_ABI +sceFontCharacterRefersTextNext(OrbisFontTextCharacter* textCharacter); +s32 PS4_SYSV_ABI sceFontCharactersRefersTextCodes(); +s32 PS4_SYSV_ABI sceFontClearDeviceCache(); +s32 PS4_SYSV_ABI sceFontCloseFont(); +s32 PS4_SYSV_ABI sceFontControl(); +s32 PS4_SYSV_ABI sceFontCreateGraphicsDevice(); +s32 PS4_SYSV_ABI sceFontCreateGraphicsService(); +s32 PS4_SYSV_ABI sceFontCreateGraphicsServiceWithEdition(); +s32 PS4_SYSV_ABI sceFontCreateLibrary(); +s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition(); +s32 PS4_SYSV_ABI sceFontCreateRenderer(); +s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition(); +s32 PS4_SYSV_ABI sceFontCreateString(); +s32 PS4_SYSV_ABI sceFontCreateWords(); +s32 PS4_SYSV_ABI sceFontCreateWritingLine(); +s32 PS4_SYSV_ABI sceFontDefineAttribute(); +s32 PS4_SYSV_ABI sceFontDeleteGlyph(); +s32 PS4_SYSV_ABI sceFontDestroyGraphicsDevice(); +s32 PS4_SYSV_ABI sceFontDestroyGraphicsService(); +s32 PS4_SYSV_ABI sceFontDestroyLibrary(); +s32 PS4_SYSV_ABI sceFontDestroyRenderer(); +s32 PS4_SYSV_ABI sceFontDestroyString(); +s32 PS4_SYSV_ABI sceFontDestroyWords(); +s32 PS4_SYSV_ABI sceFontDestroyWritingLine(); +s32 PS4_SYSV_ABI sceFontDettachDeviceCacheBuffer(); +s32 PS4_SYSV_ABI sceFontGenerateCharGlyph(); +s32 PS4_SYSV_ABI sceFontGetAttribute(); +s32 PS4_SYSV_ABI sceFontGetCharGlyphCode(); +s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics(); +s32 PS4_SYSV_ABI sceFontGetEffectSlant(); +s32 PS4_SYSV_ABI sceFontGetEffectWeight(); +s32 PS4_SYSV_ABI sceFontGetFontGlyphsCount(); +s32 PS4_SYSV_ABI sceFontGetFontGlyphsOutlineProfile(); +s32 PS4_SYSV_ABI sceFontGetFontMetrics(); +s32 PS4_SYSV_ABI sceFontGetFontResolution(); +s32 PS4_SYSV_ABI sceFontGetFontStyleInformation(); +s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState(); +s32 PS4_SYSV_ABI sceFontGetHorizontalLayout(); +s32 PS4_SYSV_ABI sceFontGetKerning(); +s32 PS4_SYSV_ABI sceFontGetLibrary(); +s32 PS4_SYSV_ABI sceFontGetPixelResolution(); +s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics(); +s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant(); +s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight(); +s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning(); +s32 PS4_SYSV_ABI sceFontGetRenderScalePixel(); +s32 PS4_SYSV_ABI sceFontGetRenderScalePoint(); +s32 PS4_SYSV_ABI sceFontGetResolutionDpi(); +s32 PS4_SYSV_ABI sceFontGetScalePixel(); +s32 PS4_SYSV_ABI sceFontGetScalePoint(); +s32 PS4_SYSV_ABI sceFontGetScriptLanguage(); +s32 PS4_SYSV_ABI sceFontGetTypographicDesign(); +s32 PS4_SYSV_ABI sceFontGetVerticalLayout(); +s32 PS4_SYSV_ABI sceFontGlyphDefineAttribute(); +s32 PS4_SYSV_ABI sceFontGlyphGetAttribute(); +s32 PS4_SYSV_ABI sceFontGlyphGetGlyphForm(); +s32 PS4_SYSV_ABI sceFontGlyphGetMetricsForm(); +s32 PS4_SYSV_ABI sceFontGlyphGetScalePixel(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetrics(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontal(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalAdvance(); +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalX(); +s32 PS4_SYSV_ABI sceFontGlyphRefersOutline(); +s32 PS4_SYSV_ABI sceFontGlyphRenderImage(); +s32 PS4_SYSV_ABI sceFontGlyphRenderImageHorizontal(); +s32 PS4_SYSV_ABI sceFontGlyphRenderImageVertical(); +s32 PS4_SYSV_ABI sceFontGraphicsBeginFrame(); +s32 PS4_SYSV_ABI sceFontGraphicsDrawingCancel(); +s32 PS4_SYSV_ABI sceFontGraphicsDrawingFinish(); +s32 PS4_SYSV_ABI sceFontGraphicsEndFrame(); +s32 PS4_SYSV_ABI sceFontGraphicsExchangeResource(); +s32 PS4_SYSV_ABI sceFontGraphicsFillMethodInit(); +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotInit(); +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetLayout(); +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetMapping(); +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesInit(); +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetFillEffect(); +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetLayout(); +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetMapping(); +s32 PS4_SYSV_ABI sceFontGraphicsGetDeviceUsage(); +s32 PS4_SYSV_ABI sceFontGraphicsRegionInit(); +s32 PS4_SYSV_ABI sceFontGraphicsRegionInitCircular(); +s32 PS4_SYSV_ABI sceFontGraphicsRegionInitRoundish(); +s32 PS4_SYSV_ABI sceFontGraphicsRelease(); +s32 PS4_SYSV_ABI sceFontGraphicsRenderResource(); +s32 PS4_SYSV_ABI sceFontGraphicsSetFramePolicy(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupClipping(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupColorRates(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupFillMethod(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupFillRates(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFill(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFillPlot(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupHandleDefault(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupLocation(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupPositioning(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupRotation(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupScaling(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFill(); +s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFillPlot(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvas(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvasSequence(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureDesign(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureDesignResource(); +s32 PS4_SYSV_ABI sceFontGraphicsStructureSurfaceTexture(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateClipping(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateColorRates(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillMethod(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillRates(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFill(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFillPlot(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateLocation(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdatePositioning(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateRotation(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateScaling(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFill(); +s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFillPlot(); +s32 PS4_SYSV_ABI sceFontMemoryInit(); +s32 PS4_SYSV_ABI sceFontMemoryTerm(); +s32 PS4_SYSV_ABI sceFontOpenFontFile(); +s32 PS4_SYSV_ABI sceFontOpenFontInstance(); +s32 PS4_SYSV_ABI sceFontOpenFontMemory(); +s32 PS4_SYSV_ABI sceFontOpenFontSet(); +s32 PS4_SYSV_ABI sceFontRebindRenderer(); +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage(); +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal(); +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical(); +s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize(); +s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer(); +s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy(); +void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface, void* buffer, + int bufWidthByte, int pixelSizeByte, int widthPixel, + int heightPixel); +void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderSurface, int x0, + int y0, int w, int h); +s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* renderSurface, + OrbisFontStyleFrame* styleFrame); +s32 PS4_SYSV_ABI sceFontSetEffectSlant(); +s32 PS4_SYSV_ABI sceFontSetEffectWeight(); +s32 PS4_SYSV_ABI sceFontSetFontsOpenMode(); +s32 PS4_SYSV_ABI sceFontSetResolutionDpi(); +s32 PS4_SYSV_ABI sceFontSetScalePixel(); +s32 PS4_SYSV_ABI sceFontSetScalePoint(); +s32 PS4_SYSV_ABI sceFontSetScriptLanguage(); +s32 PS4_SYSV_ABI sceFontSetTypographicDesign(); +s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant(); +s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight(); +s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel(); +s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint(); +s32 PS4_SYSV_ABI sceFontStringGetTerminateCode(); +s32 PS4_SYSV_ABI sceFontStringGetTerminateOrder(); +s32 PS4_SYSV_ABI sceFontStringGetWritingForm(); +s32 PS4_SYSV_ABI sceFontStringRefersRenderCharacters(); +s32 PS4_SYSV_ABI sceFontStringRefersTextCharacters(); +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(OrbisFontStyleFrame* styleFrame, + float* slantRatio); +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(OrbisFontStyleFrame* fontStyleFrame, + float* weightXScale, float* weightYScale, + uint32_t* mode); +s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi(); +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(OrbisFontStyleFrame* styleFrame, float* w, + float* h); +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePoint(); +s32 PS4_SYSV_ABI sceFontStyleFrameInit(); +s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectSlant(); +s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectWeight(); +s32 PS4_SYSV_ABI sceFontStyleFrameSetResolutionDpi(); +s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePixel(); +s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePoint(); +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectSlant(); +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectWeight(); +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale(); +s32 PS4_SYSV_ABI sceFontSupportExternalFonts(); +s32 PS4_SYSV_ABI sceFontSupportGlyphs(); +s32 PS4_SYSV_ABI sceFontSupportSystemFonts(); +s32 PS4_SYSV_ABI sceFontTextCodesStepBack(); +s32 PS4_SYSV_ABI sceFontTextCodesStepNext(); +s32 PS4_SYSV_ABI sceFontTextSourceInit(); +s32 PS4_SYSV_ABI sceFontTextSourceRewind(); +s32 PS4_SYSV_ABI sceFontTextSourceSetDefaultFont(); +s32 PS4_SYSV_ABI sceFontTextSourceSetWritingForm(); +s32 PS4_SYSV_ABI sceFontUnbindRenderer(); +s32 PS4_SYSV_ABI sceFontWordsFindWordCharacters(); +s32 PS4_SYSV_ABI sceFontWritingGetRenderMetrics(); +s32 PS4_SYSV_ABI sceFontWritingInit(); +s32 PS4_SYSV_ABI sceFontWritingLineClear(); +s32 PS4_SYSV_ABI sceFontWritingLineGetOrderingSpace(); +s32 PS4_SYSV_ABI sceFontWritingLineGetRenderMetrics(); +s32 PS4_SYSV_ABI sceFontWritingLineRefersRenderStep(); +s32 PS4_SYSV_ABI sceFontWritingLineWritesOrder(); +s32 PS4_SYSV_ABI sceFontWritingRefersRenderStep(); +s32 PS4_SYSV_ABI sceFontWritingRefersRenderStepCharacter(); +s32 PS4_SYSV_ABI sceFontWritingSetMaskInvisible(); +s32 PS4_SYSV_ABI Func_00F4D778F1C88CB3(); +s32 PS4_SYSV_ABI Func_03C650025FBB0DE7(); +s32 PS4_SYSV_ABI Func_07EAB8A163B27E1A(); +s32 PS4_SYSV_ABI Func_09408E88E4F97CE3(); +s32 PS4_SYSV_ABI Func_09F92905ED82A814(); +s32 PS4_SYSV_ABI Func_0D142CEE1AB21ABE(); +s32 PS4_SYSV_ABI Func_14BD2E9E119C16F2(); +s32 PS4_SYSV_ABI Func_1AC53C9EDEAE8D75(); +s32 PS4_SYSV_ABI Func_1D401185D5E24C3D(); +s32 PS4_SYSV_ABI Func_1E83CD20C2CC996F(); +s32 PS4_SYSV_ABI Func_314B1F765B9FE78A(); +s32 PS4_SYSV_ABI Func_350E6725FEDE29E1(); +s32 PS4_SYSV_ABI Func_3DB773F0A604BF39(); +s32 PS4_SYSV_ABI Func_4FF49DD21E311B1C(); +s32 PS4_SYSV_ABI Func_526287664A493981(); +s32 PS4_SYSV_ABI Func_55CA718DBC84A6E9(); +s32 PS4_SYSV_ABI Func_563FC5F0706A8B4D(); +s32 PS4_SYSV_ABI Func_569E2ECD34290F45(); +s32 PS4_SYSV_ABI Func_5A04775B6BE47685(); +s32 PS4_SYSV_ABI Func_5FD93BCAB6F79750(); +s32 PS4_SYSV_ABI Func_62B5398F864BD3B4(); +s32 PS4_SYSV_ABI Func_6F9010294D822367(); +s32 PS4_SYSV_ABI Func_7757E947423A7A67(); +s32 PS4_SYSV_ABI Func_7E06BA52077F54FA(); +s32 PS4_SYSV_ABI Func_93B36DEA021311D6(); +s32 PS4_SYSV_ABI Func_94B0891E7111598A(); +s32 PS4_SYSV_ABI Func_9785C9128C2FE7CD(); +s32 PS4_SYSV_ABI Func_97DFBC9B65FBC0E1(); +s32 PS4_SYSV_ABI Func_ACD9717405D7D3CA(); +s32 PS4_SYSV_ABI Func_B19A8AEC3FD4F16F(); +s32 PS4_SYSV_ABI Func_C10F488AD7CF103D(); +s32 PS4_SYSV_ABI Func_D0C8B5FF4A6826C7(); +s32 PS4_SYSV_ABI Func_E48D3CD01C342A33(); +s32 PS4_SYSV_ABI Func_EAC96B2186B71E14(); +s32 PS4_SYSV_ABI Func_FE4788A96EF46256(); +s32 PS4_SYSV_ABI Func_FE7E5AE95D3058F5(); + +void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Font \ No newline at end of file diff --git a/src/core/libraries/font/font_error.h b/src/core/libraries/font/font_error.h new file mode 100644 index 000000000..64e8fc6a7 --- /dev/null +++ b/src/core/libraries/font/font_error.h @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "core/libraries/error_codes.h" + +constexpr int ORBIS_FONT_ERROR_FATAL = 0x80460001; +constexpr int ORBIS_FONT_ERROR_INVALID_PARAMETER = 0x80460002; +constexpr int ORBIS_FONT_ERROR_INVALID_MEMORY = 0x80460003; +constexpr int ORBIS_FONT_ERROR_INVALID_LIBRARY = 0x80460004; +constexpr int ORBIS_FONT_ERROR_INVALID_FONT_HANDLE = 0x80460005; +constexpr int ORBIS_FONT_ERROR_INVALID_GLYPH = 0x80460006; +constexpr int ORBIS_FONT_ERROR_INVALID_RENDERER = 0x80460007; +constexpr int ORBIS_FONT_ERROR_INVALID_TEXT_SOURCE = 0x80460008; +constexpr int ORBIS_FONT_ERROR_INVALID_STRING = 0x80460009; +constexpr int ORBIS_FONT_ERROR_INVALID_WRITING = 0x8046000A; +constexpr int ORBIS_FONT_ERROR_INVALID_WORDS = 0x8046000B; +constexpr int ORBIS_FONT_ERROR_ALLOCATION_FAILED = 0x80460010; +constexpr int ORBIS_FONT_ERROR_FS_OPEN_FAILED = 0x80460011; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_LIBRARY = 0x80460018; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_FORMAT = 0x80460019; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_FUNCTION = 0x80460020; +constexpr int ORBIS_FONT_ERROR_ALREADY_SPECIFIED = 0x80460021; +constexpr int ORBIS_FONT_ERROR_ALREADY_ATTACHED = 0x80460022; +constexpr int ORBIS_FONT_ERROR_ALREADY_OPENED = 0x80460023; +constexpr int ORBIS_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER = 0x80460025; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_FONTSET = 0x80460031; +constexpr int ORBIS_FONT_ERROR_FONT_OPEN_MAX = 0x80460033; +constexpr int ORBIS_FONT_ERROR_FONT_OPEN_FAILED = 0x80460036; +constexpr int ORBIS_FONT_ERROR_FONT_CLOSE_FAILED = 0x80460037; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_TYPOGRAPHY = 0x80460040; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_CODE = 0x80460041; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH = 0x80460042; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_SCRIPT = 0x80460043; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_LANGUAGE = 0x80460044; +constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_SURFACE = 0x80460050; +constexpr int ORBIS_FONT_ERROR_UNSET_PARAMETER = 0x80460058; +constexpr int ORBIS_FONT_ERROR_FUNCTIONAL_LIMIT = 0x8046005C; +constexpr int ORBIS_FONT_ERROR_ALREADY_BOUND_RENDERER = 0x80460060; +constexpr int ORBIS_FONT_ERROR_NOT_BOUND_RENDERER = 0x80460061; +constexpr int ORBIS_FONT_ERROR_RENDERER_ALLOCATION_FAILED = 0x80460063; +constexpr int ORBIS_FONT_ERROR_RENDERER_ALLOCATION_LIMITED = 0x80460064; +constexpr int ORBIS_FONT_ERROR_RENDERER_RENDER_FAILED = 0x80460065; diff --git a/src/core/libraries/font/fontft.cpp b/src/core/libraries/font/fontft.cpp new file mode 100644 index 000000000..4a1dbb989 --- /dev/null +++ b/src/core/libraries/font/fontft.cpp @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/font/fontft.h" +#include "core/libraries/libs.h" + +namespace Libraries::FontFt { + +s32 PS4_SYSV_ABI sceFontFtInitAliases() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSetAliasFont() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSetAliasPath() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportBdf() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportCid() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportFontFormats() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportOpenType() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportOpenTypeOtf() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportOpenTypeTtf() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportPcf() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportPfr() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportSystemFonts() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportTrueType() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportTrueTypeGx() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportType1() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportType42() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtSupportWinFonts() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontFtTermAliases() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSelectGlyphsFt() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSelectLibraryFt() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSelectRendererFt() { + LOG_ERROR(Lib_FontFt, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceFontFt(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("e60aorDdpB8", "libSceFontFt", 1, "libSceFontFt", sceFontFtInitAliases); + LIB_FUNCTION("BxcmiMc3UaA", "libSceFontFt", 1, "libSceFontFt", sceFontFtSetAliasFont); + LIB_FUNCTION("MEWjebIzDEI", "libSceFontFt", 1, "libSceFontFt", sceFontFtSetAliasPath); + LIB_FUNCTION("ZcQL0iSjvFw", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportBdf); + LIB_FUNCTION("LADHEyFTxRQ", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportCid); + LIB_FUNCTION("+jqQjsancTs", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportFontFormats); + LIB_FUNCTION("oakL15-mBtc", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportOpenType); + LIB_FUNCTION("dcQeaDr8UJc", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportOpenTypeOtf); + LIB_FUNCTION("2KXS-HkZT3c", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportOpenTypeTtf); + LIB_FUNCTION("H0mJnhKwV-s", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportPcf); + LIB_FUNCTION("S2mw3sYplAI", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportPfr); + LIB_FUNCTION("+ehNXJPUyhk", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportSystemFonts); + LIB_FUNCTION("4BAhDLdrzUI", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportTrueType); + LIB_FUNCTION("Utlzbdf+g9o", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportTrueTypeGx); + LIB_FUNCTION("nAfQ6qaL1fU", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportType1); + LIB_FUNCTION("X9+pzrGtBus", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportType42); + LIB_FUNCTION("w0hI3xsK-hc", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportWinFonts); + LIB_FUNCTION("w5sfH9r8ZJ4", "libSceFontFt", 1, "libSceFontFt", sceFontFtTermAliases); + LIB_FUNCTION("ojW+VKl4Ehs", "libSceFontFt", 1, "libSceFontFt", sceFontSelectGlyphsFt); + LIB_FUNCTION("oM+XCzVG3oM", "libSceFontFt", 1, "libSceFontFt", sceFontSelectLibraryFt); + LIB_FUNCTION("Xx974EW-QFY", "libSceFontFt", 1, "libSceFontFt", sceFontSelectRendererFt); +}; + +} // namespace Libraries::FontFt \ No newline at end of file diff --git a/src/core/libraries/font/fontft.h b/src/core/libraries/font/fontft.h new file mode 100644 index 000000000..cec6d7872 --- /dev/null +++ b/src/core/libraries/font/fontft.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::FontFt { + +s32 PS4_SYSV_ABI sceFontFtInitAliases(); +s32 PS4_SYSV_ABI sceFontFtSetAliasFont(); +s32 PS4_SYSV_ABI sceFontFtSetAliasPath(); +s32 PS4_SYSV_ABI sceFontFtSupportBdf(); +s32 PS4_SYSV_ABI sceFontFtSupportCid(); +s32 PS4_SYSV_ABI sceFontFtSupportFontFormats(); +s32 PS4_SYSV_ABI sceFontFtSupportOpenType(); +s32 PS4_SYSV_ABI sceFontFtSupportOpenTypeOtf(); +s32 PS4_SYSV_ABI sceFontFtSupportOpenTypeTtf(); +s32 PS4_SYSV_ABI sceFontFtSupportPcf(); +s32 PS4_SYSV_ABI sceFontFtSupportPfr(); +s32 PS4_SYSV_ABI sceFontFtSupportSystemFonts(); +s32 PS4_SYSV_ABI sceFontFtSupportTrueType(); +s32 PS4_SYSV_ABI sceFontFtSupportTrueTypeGx(); +s32 PS4_SYSV_ABI sceFontFtSupportType1(); +s32 PS4_SYSV_ABI sceFontFtSupportType42(); +s32 PS4_SYSV_ABI sceFontFtSupportWinFonts(); +s32 PS4_SYSV_ABI sceFontFtTermAliases(); +s32 PS4_SYSV_ABI sceFontSelectGlyphsFt(); +s32 PS4_SYSV_ABI sceFontSelectLibraryFt(); +s32 PS4_SYSV_ABI sceFontSelectRendererFt(); + +void RegisterlibSceFontFt(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::FontFt \ No newline at end of file diff --git a/src/emulator.cpp b/src/emulator.cpp index f9e1f7959..0e7485b89 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -31,6 +31,8 @@ #include "core/file_format/trp.h" #include "core/file_sys/fs.h" #include "core/libraries/disc_map/disc_map.h" +#include "core/libraries/font/font.h" +#include "core/libraries/font/fontft.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libs.h" #include "core/libraries/ngs2/ngs2.h" @@ -473,8 +475,8 @@ void Emulator::LoadSystemModules(const std::string& game_serial) { {"libSceJson2.sprx", nullptr}, {"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterLib}, {"libSceCesCs.sprx", nullptr}, - {"libSceFont.sprx", nullptr}, - {"libSceFontFt.sprx", nullptr}, + {"libSceFont.sprx", &Libraries::Font::RegisterlibSceFont}, + {"libSceFontFt.sprx", &Libraries::FontFt::RegisterlibSceFontFt}, {"libSceFreeTypeOt.sprx", nullptr}}); std::vector found_modules; From 7031f5968e2d76b469f6ff2265d64e73f3feaacb Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:32:24 -0600 Subject: [PATCH 04/25] Better return stub (#3773) Gets THE PLAYROOM (CUSA00001) further. --- src/core/libraries/network/net.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index d9ef76afc..97005813b 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -1285,7 +1285,8 @@ u16 PS4_SYSV_ABI sceNetNtohs(u16 net16) { int PS4_SYSV_ABI sceNetPoolCreate(const char* name, int size, int flags) { LOG_ERROR(Lib_Net, "(DUMMY) name = {} size = {} flags = {} ", std::string(name), size, flags); - return ORBIS_OK; + static s32 id = 1; + return id++; } int PS4_SYSV_ABI sceNetPoolDestroy() { From 19e974bf21bc1d5ac9483c5db382bca72a7545d4 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Wed, 5 Nov 2025 18:58:15 -0600 Subject: [PATCH 05/25] Libkernel: Implement/stub some functions (#3774) * Define latest released firmware version, use that for sceKernelGetSystemSwVersion I feel this is less hacky and error-prone than just returning the game firmware. * sceKernelGetAllowedSdkVersionOnSystem * sceKernelHasNeoMode * sceKernelGetAppInfo stub * sceKernelGetCurrentCpu * fixups * sceKernelGetMainSocId Used by libSceAvPlayer to determine if console is a pro or not. * Update process.cpp * Set has_param_sfo to true * Clang --- src/core/libraries/kernel/kernel.cpp | 43 +++++++++++++++++++++++---- src/core/libraries/kernel/kernel.h | 26 ++++++++++++++++ src/core/libraries/kernel/process.cpp | 22 ++++++++++++++ 3 files changed, 85 insertions(+), 6 deletions(-) diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index 054f34c15..6594bfab2 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -236,15 +236,24 @@ s32 PS4_SYSV_ABI sceKernelSetGPO() { return ORBIS_OK; } +s32 PS4_SYSV_ABI sceKernelGetAllowedSdkVersionOnSystem(s32* ver) { + if (ver == nullptr) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + // Returns the highest game SDK version this PS4 allows. + *ver = CURRENT_FIRMWARE_VERSION | 0xfff; + LOG_INFO(Lib_Kernel, "called, returned sw version: {}", *ver); + return ORBIS_OK; +} + s32 PS4_SYSV_ABI sceKernelGetSystemSwVersion(SwVersionStruct* ret) { if (ret == nullptr) { - return ORBIS_OK; // but why? + return ORBIS_OK; } - ASSERT(ret->struct_size == 40); - u32 fake_fw = Common::ElfInfo::Instance().RawFirmwareVer(); + u32 fake_fw = CURRENT_FIRMWARE_VERSION; ret->hex_representation = fake_fw; std::snprintf(ret->text_representation, 28, "%2x.%03x.%03x", fake_fw >> 0x18, - fake_fw >> 0xc & 0xfff, fake_fw & 0xfff); // why %2x? + fake_fw >> 0xc & 0xfff, fake_fw & 0xfff); LOG_INFO(Lib_Kernel, "called, returned sw version: {}", ret->text_representation); return ORBIS_OK; } @@ -257,9 +266,13 @@ const char** PS4_SYSV_ABI getargv() { return entry_params.argv; } -s32 PS4_SYSV_ABI get_authinfo(int pid, AuthInfoData* p2) { +s32 PS4_SYSV_ABI get_authinfo(s32 pid, AuthInfoData* p2) { LOG_WARNING(Lib_Kernel, "(STUBBED) called, pid: {}", pid); - if ((pid != 0) && (pid != GLOBAL_PID)) { + if (p2 == nullptr) { + *Kernel::__Error() = POSIX_EPERM; + return -1; + } + if (pid != 0 && pid != GLOBAL_PID) { *Kernel::__Error() = POSIX_ESRCH; return -1; } @@ -269,6 +282,22 @@ s32 PS4_SYSV_ABI get_authinfo(int pid, AuthInfoData* p2) { return ORBIS_OK; } +s32 PS4_SYSV_ABI sceKernelGetAppInfo(s32 pid, OrbisKernelAppInfo* app_info) { + LOG_WARNING(Lib_Kernel, "(STUBBED) called, pid: {}", pid); + if (pid != GLOBAL_PID) { + return ORBIS_KERNEL_ERROR_EPERM; + } + if (app_info == nullptr) { + return ORBIS_OK; + } + + auto& game_info = Common::ElfInfo::Instance(); + *app_info = {}; + app_info->has_param_sfo = 1; + strncpy(app_info->cusa_name, game_info.GameSerial().data(), 10); + return ORBIS_OK; +} + void RegisterLib(Core::Loader::SymbolsResolver* sym) { service_thread = std::jthread{KernelServiceThread}; @@ -285,8 +314,10 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) { LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", &g_stack_chk_guard); LIB_FUNCTION("D4yla3vx4tY", "libkernel", 1, "libkernel", sceKernelError); + LIB_FUNCTION("YeU23Szo3BM", "libkernel", 1, "libkernel", sceKernelGetAllowedSdkVersionOnSystem); LIB_FUNCTION("Mv1zUObHvXI", "libkernel", 1, "libkernel", sceKernelGetSystemSwVersion); LIB_FUNCTION("igMefp4SAv0", "libkernel", 1, "libkernel", get_authinfo); + LIB_FUNCTION("G-MYv5erXaU", "libkernel", 1, "libkernel", sceKernelGetAppInfo); LIB_FUNCTION("PfccT7qURYE", "libkernel", 1, "libkernel", kernel_ioctl); LIB_FUNCTION("wW+k21cmbwQ", "libkernel", 1, "libkernel", kernel_ioctl); LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", sceKernelGetFsSandboxRandomWord); diff --git a/src/core/libraries/kernel/kernel.h b/src/core/libraries/kernel/kernel.h index 74c457dc5..91fac4c71 100644 --- a/src/core/libraries/kernel/kernel.h +++ b/src/core/libraries/kernel/kernel.h @@ -36,6 +36,8 @@ struct OrbisWrapperImpl { #define ORBIS(func) (Libraries::Kernel::OrbisWrapperImpl::wrap) +#define CURRENT_FIRMWARE_VERSION 0x13020011 + s32* PS4_SYSV_ABI __Error(); struct SwVersionStruct { @@ -51,6 +53,30 @@ struct AuthInfoData { u64 ucred[8]; }; +struct OrbisKernelTitleWorkaround { + s32 version; + s32 align; + u64 ids[2]; +}; + +struct OrbisKernelAppInfo { + s32 app_id; + s32 mmap_flags; + s32 attribute_exe; + s32 attribute2; + char cusa_name[10]; + u8 debug_level; + u8 slv_flags; + u8 mini_app_dmem_flags; + u8 render_mode; + u8 mdbg_out; + u8 required_hdcp_type; + u64 preload_prx_flags; + s32 attribute1; + s32 has_param_sfo; + OrbisKernelTitleWorkaround title_workaround; +}; + void RegisterLib(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index 0e168e43a..8220e55d8 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -21,6 +21,20 @@ s32 PS4_SYSV_ABI sceKernelIsNeoMode() { Common::ElfInfo::Instance().GetPSFAttributes().support_neo_mode; } +s32 PS4_SYSV_ABI sceKernelHasNeoMode() { + return Config::isNeoModeConsole(); +} + +s32 PS4_SYSV_ABI sceKernelGetMainSocId() { + // These hardcoded values are based on hardware observations. + // Different models of PS4/PS4 Pro likely return slightly different values. + LOG_DEBUG(Lib_Kernel, "called"); + if (Config::isNeoModeConsole()) { + return 0x740f30; + } + return 0x710f10; +} + s32 PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(s32* ver) { s32 version = Common::ElfInfo::Instance().RawFirmwareVer(); *ver = version; @@ -31,6 +45,11 @@ s32 PS4_SYSV_ABI sceKernelGetCpumode() { return 0; } +s32 PS4_SYSV_ABI sceKernelGetCurrentCpu() { + LOG_DEBUG(Lib_Kernel, "called"); + return 0; +} + void* PS4_SYSV_ABI sceKernelGetProcParam() { auto* linker = Common::Singleton::Instance(); return linker->GetProcParam(); @@ -208,7 +227,10 @@ void RegisterProcess(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("xeu-pV8wkKs", "libkernel", 1, "libkernel", sceKernelIsInSandbox); LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", sceKernelGetCompiledSdkVersion); LIB_FUNCTION("WslcK1FQcGI", "libkernel", 1, "libkernel", sceKernelIsNeoMode); + LIB_FUNCTION("rNRtm1uioyY", "libkernel", 1, "libkernel", sceKernelHasNeoMode); + LIB_FUNCTION("0vTn5IDMU9A", "libkernel", 1, "libkernel", sceKernelGetMainSocId); LIB_FUNCTION("VOx8NGmHXTs", "libkernel", 1, "libkernel", sceKernelGetCpumode); + LIB_FUNCTION("g0VTBxfJyu0", "libkernel", 1, "libkernel", sceKernelGetCurrentCpu); LIB_FUNCTION("959qrazPIrg", "libkernel", 1, "libkernel", sceKernelGetProcParam); LIB_FUNCTION("wzvqT4UqKX8", "libkernel", 1, "libkernel", sceKernelLoadStartModule); LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", sceKernelDlsym); From 604f8d9398c735964d7463c09ff42b0d1528470c Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Thu, 6 Nov 2025 12:22:26 +0100 Subject: [PATCH 06/25] More OpenOrbis stuff (#3776) --- src/core/libraries/kernel/threads/condvar.cpp | 2 ++ src/core/libraries/kernel/threads/mutex.cpp | 2 ++ src/core/libraries/kernel/threads/pthread.cpp | 3 +++ 3 files changed, 7 insertions(+) diff --git a/src/core/libraries/kernel/threads/condvar.cpp b/src/core/libraries/kernel/threads/condvar.cpp index 98f7397ad..9d429ed7d 100644 --- a/src/core/libraries/kernel/threads/condvar.cpp +++ b/src/core/libraries/kernel/threads/condvar.cpp @@ -354,6 +354,8 @@ void RegisterCond(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("Op8TBGY5KHg", "libkernel", 1, "libkernel", posix_pthread_cond_wait); LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", posix_pthread_cond_broadcast); LIB_FUNCTION("2MOy+rUfuhQ", "libkernel", 1, "libkernel", posix_pthread_cond_signal); + LIB_FUNCTION("RXXqi4CtF8w", "libkernel", 1, "libkernel", posix_pthread_cond_destroy); + LIB_FUNCTION("27bAgiJmOh0", "libkernel", 1, "libkernel", posix_pthread_cond_timedwait); LIB_FUNCTION("mKoTx03HRWA", "libkernel", 1, "libkernel", posix_pthread_condattr_init); LIB_FUNCTION("dJcuQVn6-Iw", "libkernel", 1, "libkernel", posix_pthread_condattr_destroy); diff --git a/src/core/libraries/kernel/threads/mutex.cpp b/src/core/libraries/kernel/threads/mutex.cpp index 505bd0d9d..5d97c5dc1 100644 --- a/src/core/libraries/kernel/threads/mutex.cpp +++ b/src/core/libraries/kernel/threads/mutex.cpp @@ -442,6 +442,8 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("ltCfaGr2JGE", "libkernel", 1, "libkernel", posix_pthread_mutex_destroy); LIB_FUNCTION("dQHWEsJtoE4", "libkernel", 1, "libkernel", posix_pthread_mutexattr_init); LIB_FUNCTION("mDmgMOGVUqg", "libkernel", 1, "libkernel", posix_pthread_mutexattr_settype); + LIB_FUNCTION("HF7lK46xzjY", "libkernel", 1, "libkernel", posix_pthread_mutexattr_destroy); + LIB_FUNCTION("K-jXhbt2gn4", "libkernel", 1, "libkernel", posix_pthread_mutex_trylock); // Orbis LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", ORBIS(scePthreadMutexInit)); diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 09e8b9558..8ab8b72c3 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -663,6 +663,9 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", posix_pthread_once); LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", posix_pthread_self); LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", posix_pthread_create); + LIB_FUNCTION("+U1R4WtXvoc", "libkernel", 1, "libkernel", posix_pthread_detach); + LIB_FUNCTION("7Xl257M4VNI", "libkernel", 1, "libkernel", posix_pthread_equal); + LIB_FUNCTION("h9CcP3J0oVM", "libkernel", 1, "libkernel", posix_pthread_join); LIB_FUNCTION("Jb2uGFMr688", "libkernel", 1, "libkernel", posix_pthread_getaffinity_np); LIB_FUNCTION("5KWrg7-ZqvE", "libkernel", 1, "libkernel", posix_pthread_setaffinity_np); LIB_FUNCTION("3eqs37G74-s", "libkernel", 1, "libkernel", posix_pthread_getthreadid_np); From 8c1ec863da26dc417ef8d16b7c20698e5a465be3 Mon Sep 17 00:00:00 2001 From: Joshua de Reeper Date: Thu, 6 Nov 2025 09:34:52 -0500 Subject: [PATCH 07/25] Add Infinity Base Backend (#3778) --- CMakeLists.txt | 2 + src/core/libraries/usbd/emulated/infinity.cpp | 392 ++++++++++++++++++ src/core/libraries/usbd/emulated/infinity.h | 112 +++++ src/core/libraries/usbd/usbd.h | 3 +- 4 files changed, 508 insertions(+), 1 deletion(-) create mode 100644 src/core/libraries/usbd/emulated/infinity.cpp create mode 100644 src/core/libraries/usbd/emulated/infinity.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b75da04d6..d1979a934 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -539,6 +539,8 @@ set(RANDOM_LIB src/core/libraries/random/random.cpp set(USBD_LIB src/core/libraries/usbd/usbd.cpp src/core/libraries/usbd/usbd.h src/core/libraries/usbd/usb_backend.h + src/core/libraries/usbd/emulated/infinity.cpp + src/core/libraries/usbd/emulated/infinity.h src/core/libraries/usbd/emulated/skylander.cpp src/core/libraries/usbd/emulated/skylander.h ) diff --git a/src/core/libraries/usbd/emulated/infinity.cpp b/src/core/libraries/usbd/emulated/infinity.cpp new file mode 100644 index 000000000..af8e13dcc --- /dev/null +++ b/src/core/libraries/usbd/emulated/infinity.cpp @@ -0,0 +1,392 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "infinity.h" + +#include + +namespace Libraries::Usbd { + +InfinityBase::InfinityBase() {} + +void InfinityBase::LoadFigure(std::string file_name, u8 pad, u8 slot) { + Common::FS::IOFile file(file_name, Common::FS::FileAccessMode::ReadWrite); + std::array data; + ASSERT(file.Read(data) == data.size()); + LoadInfinityFigure(data, std::move(file), slot); +} + +void InfinityBase::RemoveFigure(u8 pad, u8 slot, bool full_remove) { + std::lock_guard lock(infinity_mutex); + InfinityFigure& figure = infinity_figures[slot]; + + if (!figure.present) { + return; + } + + slot = DeriveFigurePosition(slot); + if (slot == 0) { + return; + } + + figure.present = false; + + std::array figure_change_response = {0xab, 0x04, slot, 0x09, figure.order_added, 0x01}; + figure_change_response[6] = GenerateChecksum(figure_change_response, 6); + m_figure_added_removed_responses.push(figure_change_response); + + figure.Save(); + figure.infFile.Close(); +} + +void InfinityBase::LoadInfinityFigure(const std::array& buf, + Common::FS::IOFile file, u8 position) { + std::lock_guard lock(infinity_mutex); + u8 order_added; + + InfinityFigure& figure = infinity_figures[position]; + + figure.infFile = std::move(file); + memcpy(figure.data.data(), buf.data(), figure.data.size()); + figure.present = true; + if (figure.order_added == 255) { + figure.order_added = m_figure_order; + m_figure_order++; + } + order_added = figure.order_added; + + position = DeriveFigurePosition(position); + if (position == 0) { + return; + } + + std::array figure_change_response = {0xab, 0x04, position, 0x09, order_added, 0x00}; + figure_change_response[6] = GenerateChecksum(figure_change_response, 6); + m_figure_added_removed_responses.push(figure_change_response); +} + +void InfinityBase::GetBlankResponse(u8 sequence, std::array& reply_buf) { + reply_buf[0] = 0xaa; + reply_buf[1] = 0x01; + reply_buf[2] = sequence; + reply_buf[3] = GenerateChecksum(reply_buf, 3); +} + +void InfinityBase::DescrambleAndSeed(u8* buf, u8 sequence, std::array& reply_buf) { + u64 value = u64(buf[4]) << 56 | u64(buf[5]) << 48 | u64(buf[6]) << 40 | u64(buf[7]) << 32 | + u64(buf[8]) << 24 | u64(buf[9]) << 16 | u64(buf[10]) << 8 | u64(buf[11]); + u32 seed = Descramble(value); + GenerateSeed(seed); + GetBlankResponse(sequence, reply_buf); +} + +void InfinityBase::GetNextAndScramble(u8 sequence, std::array& reply_buf) { + const u32 next_random = GetNext(); + const u64 scrambled_next_random = Scramble(next_random, 0); + reply_buf = {0xAA, 0x09, sequence}; + reply_buf[3] = u8((scrambled_next_random >> 56) & 0xFF); + reply_buf[4] = u8((scrambled_next_random >> 48) & 0xFF); + reply_buf[5] = u8((scrambled_next_random >> 40) & 0xFF); + reply_buf[6] = u8((scrambled_next_random >> 32) & 0xFF); + reply_buf[7] = u8((scrambled_next_random >> 24) & 0xFF); + reply_buf[8] = u8((scrambled_next_random >> 16) & 0xFF); + reply_buf[9] = u8((scrambled_next_random >> 8) & 0xFF); + reply_buf[10] = u8(scrambled_next_random & 0xFF); + reply_buf[11] = GenerateChecksum(reply_buf, 11); +} + +void InfinityBase::GetPresentFigures(u8 sequence, std::array& reply_buf) { + int x = 3; + for (u8 i = 0; i < infinity_figures.size(); i++) { + u8 slot = i == 0 ? 0x10 : (i < 4) ? 0x20 : 0x30; + if (infinity_figures[i].present) { + reply_buf[x] = slot + infinity_figures[i].order_added; + reply_buf[x + 1] = 0x09; + x += 2; + } + } + reply_buf[0] = 0xaa; + reply_buf[1] = x - 2; + reply_buf[2] = sequence; + reply_buf[x] = GenerateChecksum(reply_buf, x); +} + +void InfinityBase::QueryBlock(u8 fig_num, u8 block, std::array& reply_buf, u8 sequence) { + std::lock_guard lock(infinity_mutex); + + InfinityFigure& figure = GetFigureByOrder(fig_num); + + reply_buf[0] = 0xaa; + reply_buf[1] = 0x12; + reply_buf[2] = sequence; + reply_buf[3] = 0x00; + const u8 file_block = (block == 0) ? 1 : (block * 4); + if (figure.present && file_block < 20) { + memcpy(&reply_buf[4], figure.data.data() + (16 * file_block), 16); + } + reply_buf[20] = GenerateChecksum(reply_buf, 20); +} + +void InfinityBase::WriteBlock(u8 fig_num, u8 block, const u8* to_write_buf, + std::array& reply_buf, u8 sequence) { + std::lock_guard lock(infinity_mutex); + + InfinityFigure& figure = GetFigureByOrder(fig_num); + + reply_buf[0] = 0xaa; + reply_buf[1] = 0x02; + reply_buf[2] = sequence; + reply_buf[3] = 0x00; + const u8 file_block = (block == 0) ? 1 : (block * 4); + if (figure.present && file_block < 20) { + memcpy(figure.data.data() + (file_block * 16), to_write_buf, 16); + figure.Save(); + } + reply_buf[4] = GenerateChecksum(reply_buf, 4); +} + +void InfinityBase::GetFigureIdentifier(u8 fig_num, u8 sequence, std::array& reply_buf) { + std::lock_guard lock(infinity_mutex); + + InfinityFigure& figure = GetFigureByOrder(fig_num); + + reply_buf[0] = 0xaa; + reply_buf[1] = 0x09; + reply_buf[2] = sequence; + reply_buf[3] = 0x00; + + if (figure.present) { + memcpy(&reply_buf[4], figure.data.data(), 7); + } + reply_buf[11] = GenerateChecksum(reply_buf, 11); +} + +std::optional> InfinityBase::PopAddedRemovedResponse() { + if (m_figure_added_removed_responses.empty()) + return std::nullopt; + + std::array response = m_figure_added_removed_responses.front(); + m_figure_added_removed_responses.pop(); + return response; +} + +u8 InfinityBase::GenerateChecksum(const std::array& data, int num_of_bytes) const { + int checksum = 0; + for (int i = 0; i < num_of_bytes; i++) { + checksum += data[i]; + } + return (checksum & 0xFF); +} + +u32 InfinityBase::Descramble(u64 num_to_descramble) { + u64 mask = 0x8E55AA1B3999E8AA; + u32 ret = 0; + + for (int i = 0; i < 64; i++) { + if (mask & 0x8000000000000000) { + ret = (ret << 1) | (num_to_descramble & 0x01); + } + + num_to_descramble >>= 1; + mask <<= 1; + } + + return ret; +} + +u64 InfinityBase::Scramble(u32 num_to_scramble, u32 garbage) { + u64 mask = 0x8E55AA1B3999E8AA; + u64 ret = 0; + + for (int i = 0; i < 64; i++) { + ret <<= 1; + + if ((mask & 1) != 0) { + ret |= (num_to_scramble & 1); + num_to_scramble >>= 1; + } else { + ret |= (garbage & 1); + garbage >>= 1; + } + + mask >>= 1; + } + + return ret; +} + +void InfinityBase::GenerateSeed(u32 seed) { + random_a = 0xF1EA5EED; + random_b = seed; + random_c = seed; + random_d = seed; + + for (int i = 0; i < 23; i++) { + GetNext(); + } +} + +u32 InfinityBase::GetNext() { + u32 a = random_a; + u32 b = random_b; + u32 c = random_c; + u32 ret = std::rotl(random_b, 27); + + const u32 temp = (a + ((ret ^ 0xFFFFFFFF) + 1)); + b ^= std::rotl(c, 17); + a = random_d; + c += a; + ret = b + temp; + a += temp; + + random_c = a; + random_a = b; + random_b = c; + random_d = ret; + + return ret; +} + +InfinityFigure& InfinityBase::GetFigureByOrder(u8 order_added) { + for (u8 i = 0; i < infinity_figures.size(); i++) { + if (infinity_figures[i].order_added == order_added) { + return infinity_figures[i]; + } + } + return infinity_figures[0]; +} + +u8 InfinityBase::DeriveFigurePosition(u8 position) { + switch (position) { + case 0: + case 1: + case 2: + return 1; + case 3: + case 4: + case 5: + return 2; + case 6: + case 7: + case 8: + return 3; + + default: + return 0; + } +} + +libusb_endpoint_descriptor* InfinityBackend::FillEndpointDescriptorPair() { + return m_endpoint_descriptors.data(); +} + +libusb_interface_descriptor* InfinityBackend::FillInterfaceDescriptor( + libusb_endpoint_descriptor* descs) { + m_interface_descriptors[0].endpoint = descs; + return m_interface_descriptors.data(); +} + +libusb_config_descriptor* InfinityBackend::FillConfigDescriptor(libusb_interface* inter) { + m_config_descriptors[0].interface = inter; + return m_config_descriptors.data(); +} + +libusb_device_descriptor* InfinityBackend::FillDeviceDescriptor() { + return m_device_descriptors.data(); +} + +libusb_transfer_status InfinityBackend::HandleAsyncTransfer(libusb_transfer* transfer) { + switch (transfer->endpoint) { + case 0x81: { + // Respond after FF command + std::optional> response = m_infinity_base->PopAddedRemovedResponse(); + if (response) { + memcpy(transfer->buffer, response.value().data(), 0x20); + } else if (!m_queries.empty()) { + memcpy(transfer->buffer, m_queries.front().data(), 0x20); + m_queries.pop(); + } + break; + } + case 0x01: { + const u8 command = transfer->buffer[2]; + const u8 sequence = transfer->buffer[3]; + LOG_INFO(Lib_Usbd, "Infinity Backend Transfer command: {:x}", command); + + std::array q_result{}; + + switch (command) { + case 0x80: { + q_result = {0xaa, 0x15, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x03, 0x02, 0x09, 0x09, 0x43, + 0x20, 0x32, 0x62, 0x36, 0x36, 0x4b, 0x34, 0x99, 0x67, 0x31, 0x93, 0x8c}; + break; + } + case 0x81: { + // Initiate Challenge + m_infinity_base->DescrambleAndSeed(transfer->buffer, sequence, q_result); + break; + } + case 0x83: { + // Challenge Response + m_infinity_base->GetNextAndScramble(sequence, q_result); + break; + } + case 0x90: + case 0x92: + case 0x93: + case 0x95: + case 0x96: { + // Color commands + m_infinity_base->GetBlankResponse(sequence, q_result); + break; + } + case 0xA1: { + // Get Present Figures + m_infinity_base->GetPresentFigures(sequence, q_result); + break; + } + case 0xA2: { + // Read Block from Figure + m_infinity_base->QueryBlock(transfer->buffer[4], transfer->buffer[5], q_result, + sequence); + break; + } + case 0xA3: { + // Write block to figure + m_infinity_base->WriteBlock(transfer->buffer[4], transfer->buffer[5], + &transfer->buffer[7], q_result, sequence); + break; + } + case 0xB4: { + // Get figure ID + m_infinity_base->GetFigureIdentifier(transfer->buffer[4], sequence, q_result); + break; + } + case 0xB5: { + // Get status? + m_infinity_base->GetBlankResponse(sequence, q_result); + break; + } + default: + LOG_ERROR(Lib_Usbd, "Unhandled Infinity Query: {}", command); + break; + } + + m_queries.push(q_result); + break; + } + default: + LOG_ERROR(Lib_Usbd, "Unhandled Infinity Endpoint: {}", transfer->endpoint); + break; + } + return LIBUSB_TRANSFER_COMPLETED; +} + +void InfinityFigure::Save() { + if (!infFile.IsOpen()) + return; + + infFile.Seek(0); + infFile.Write(data); +} +} // namespace Libraries::Usbd \ No newline at end of file diff --git a/src/core/libraries/usbd/emulated/infinity.h b/src/core/libraries/usbd/emulated/infinity.h new file mode 100644 index 000000000..4c8a5305f --- /dev/null +++ b/src/core/libraries/usbd/emulated/infinity.h @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "common/io_file.h" +#include "core/libraries/usbd/usb_backend.h" + +namespace Libraries::Usbd { + +constexpr u16 INFINITY_BLOCK_COUNT = 0x14; +constexpr u16 INFINITY_BLOCK_SIZE = 0x10; +constexpr u16 INFINITY_FIGURE_SIZE = INFINITY_BLOCK_COUNT * INFINITY_BLOCK_SIZE; +constexpr u8 MAX_INFINITY_FIGURES = 9; + +struct InfinityFigure final { + Common::FS::IOFile infFile; + std::array data{}; + bool present = false; + u8 order_added = 255; + void Save(); +}; + +class InfinityBase final : public UsbEmulatedImpl { +public: + InfinityBase(); + ~InfinityBase() override = default; + + void GetBlankResponse(u8 sequence, std::array& reply_buf); + void DescrambleAndSeed(u8* buf, u8 sequence, std::array& reply_buf); + void GetNextAndScramble(u8 sequence, std::array& reply_buf); + void GetPresentFigures(u8 sequence, std::array& reply_buf); + void QueryBlock(u8 fig_num, u8 block, std::array& reply_buf, u8 sequence); + void WriteBlock(u8 fig_num, u8 block, const u8* to_write_buf, std::array& reply_buf, + u8 sequence); + void GetFigureIdentifier(u8 fig_num, u8 sequence, std::array& reply_buf); + std::optional> PopAddedRemovedResponse(); + + void LoadFigure(std::string file_name, u8 pad, u8 slot) override; + void RemoveFigure(u8 pad, u8 slot, bool full_remove) override; + void MoveFigure(u8 new_pad, u8 new_index, u8 old_pad, u8 old_index) override {} + void TempRemoveFigure(u8 index) override {} + void CancelRemoveFigure(u8 index) override {} + + void LoadInfinityFigure(const std::array& buf, Common::FS::IOFile file, + u8 position); + +protected: + std::mutex infinity_mutex; + std::array infinity_figures; + +private: + u8 GenerateChecksum(const std::array& data, int num_of_bytes) const; + u32 Descramble(u64 num_to_descramble); + u64 Scramble(u32 num_to_scramble, u32 garbage); + void GenerateSeed(u32 seed); + u32 GetNext(); + InfinityFigure& GetFigureByOrder(u8 order_added); + u8 DeriveFigurePosition(u8 position); + + u32 random_a = 0; + u32 random_b = 0; + u32 random_c = 0; + u32 random_d = 0; + + u8 m_figure_order = 0; + std::queue> m_figure_added_removed_responses; +}; + +class InfinityBackend final : public UsbEmulatedBackend { +protected: + libusb_endpoint_descriptor* FillEndpointDescriptorPair() override; + libusb_interface_descriptor* FillInterfaceDescriptor( + libusb_endpoint_descriptor* descs) override; + libusb_config_descriptor* FillConfigDescriptor(libusb_interface* inter) override; + libusb_device_descriptor* FillDeviceDescriptor() override; + + s32 ControlTransfer(libusb_device_handle* dev_handle, u8 bmRequestType, u8 bRequest, u16 wValue, + u16 wIndex, u8* data, u16 wLength, u32 timeout) override { + return LIBUSB_SUCCESS; + } + + libusb_transfer_status HandleAsyncTransfer(libusb_transfer* transfer) override; + + std::shared_ptr GetImplRef() override { + return m_infinity_base; + } + +private: + std::shared_ptr m_infinity_base = std::make_shared(); + + std::array m_endpoint_out_extra = {0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x1d, 0x00}; + std::vector m_endpoint_descriptors = { + {0x7, 0x5, 0x81, 0x3, 0x20, 0x1, 0x0, 0x0}, + {0x7, 0x5, 0x1, 0x3, 0x20, 0x1, 0x0, 0x0, m_endpoint_out_extra.data(), 9}}; + std::vector m_interface_descriptors = { + {0x9, 0x4, 0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0}}; + std::vector m_config_descriptors = { + {0x9, 0x2, 0x29, 0x1, 0x1, 0x0, 0x80, 0xFA}}; + std::vector m_device_descriptors = { + {0x12, 0x1, 0x200, 0x0, 0x0, 0x0, 0x20, 0x0E6F, 0x0129, 0x200, 0x1, 0x2, 0x3, 0x1}}; + + std::queue> m_queries; +}; +} // namespace Libraries::Usbd \ No newline at end of file diff --git a/src/core/libraries/usbd/usbd.h b/src/core/libraries/usbd/usbd.h index 60a3e753b..f189e0de2 100644 --- a/src/core/libraries/usbd/usbd.h +++ b/src/core/libraries/usbd/usbd.h @@ -4,6 +4,7 @@ #pragma once #include "common/types.h" +#include "emulated/infinity.h" #include "emulated/skylander.h" #include "usb_backend.h" @@ -35,7 +36,7 @@ using SceUsbdTransferCallback = void PS4_SYSV_ABI (*)(SceUsbdTransfer* transfer) // TODO: implement emulated devices using SkylandersPortalBackend = SkylanderBackend; -using InfinityBaseBackend = UsbRealBackend; +using InfinityBaseBackend = InfinityBackend; using DimensionsToypadBackend = UsbRealBackend; enum class SceUsbdSpeed : u32 { From f5505daaca2323c83cae3b4b9699a109576a90a5 Mon Sep 17 00:00:00 2001 From: Joshua de Reeper Date: Thu, 6 Nov 2025 10:46:43 -0500 Subject: [PATCH 08/25] usbd: Emulate Dimensions Toypad (#3779) * Dimensions Toypad * update comment --- CMakeLists.txt | 2 + .../libraries/usbd/emulated/dimensions.cpp | 651 ++++++++++++++++++ src/core/libraries/usbd/emulated/dimensions.h | 118 ++++ src/core/libraries/usbd/usbd.h | 4 +- 4 files changed, 773 insertions(+), 2 deletions(-) create mode 100644 src/core/libraries/usbd/emulated/dimensions.cpp create mode 100644 src/core/libraries/usbd/emulated/dimensions.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d1979a934..95475d0c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -539,6 +539,8 @@ set(RANDOM_LIB src/core/libraries/random/random.cpp set(USBD_LIB src/core/libraries/usbd/usbd.cpp src/core/libraries/usbd/usbd.h src/core/libraries/usbd/usb_backend.h + src/core/libraries/usbd/emulated/dimensions.cpp + src/core/libraries/usbd/emulated/dimensions.h src/core/libraries/usbd/emulated/infinity.cpp src/core/libraries/usbd/emulated/infinity.h src/core/libraries/usbd/emulated/skylander.cpp diff --git a/src/core/libraries/usbd/emulated/dimensions.cpp b/src/core/libraries/usbd/emulated/dimensions.cpp new file mode 100644 index 000000000..272f2f649 --- /dev/null +++ b/src/core/libraries/usbd/emulated/dimensions.cpp @@ -0,0 +1,651 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "dimensions.h" + +#include +#include + +namespace Libraries::Usbd { + +static constexpr std::array COMMAND_KEY = {0x55, 0xFE, 0xF6, 0xB0, 0x62, 0xBF, 0x0B, 0x41, + 0xC9, 0xB3, 0x7C, 0xB4, 0x97, 0x3E, 0x29, 0x7B}; + +static constexpr std::array CHAR_CONSTANT = {0xB7, 0xD5, 0xD7, 0xE6, 0xE7, 0xBA, + 0x3C, 0xA8, 0xD8, 0x75, 0x47, 0x68, + 0xCF, 0x23, 0xE9, 0xFE, 0xAA}; + +static constexpr std::array PWD_CONSTANT = { + 0x28, 0x63, 0x29, 0x20, 0x43, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, + 0x20, 0x4C, 0x45, 0x47, 0x4F, 0x20, 0x32, 0x30, 0x31, 0x34, 0xAA, 0xAA}; + +DimensionsToypad::DimensionsToypad() {} + +void DimensionsToypad::LoadFigure(std::string file_name, u8 pad, u8 index) { + Common::FS::IOFile file(file_name, Common::FS::FileAccessMode::ReadWrite); + std::array data; + ASSERT(file.Read(data) == data.size()); + LoadDimensionsFigure(data, std::move(file), pad, index); +} + +u32 DimensionsToypad::LoadDimensionsFigure(const std::array& buf, + Common::FS::IOFile file, u8 pad, u8 index) { + std::lock_guard lock(m_dimensions_mutex); + + const u32 id = GetFigureId(buf); + + DimensionsFigure& figure = GetFigureByIndex(index); + figure.dimFile = std::move(file); + figure.id = id; + figure.pad = pad; + figure.index = index + 1; + figure.data = buf; + // When a figure is added to the toypad, respond to the game with the pad they were added to, + // their index, the direction (0x00 in byte 6 for added) and their UID + std::array figureChangeResponse = {0x56, 0x0b, figure.pad, 0x00, figure.index, + 0x00, buf[0], buf[1], buf[2], buf[4], + buf[5], buf[6], buf[7]}; + figureChangeResponse[13] = GenerateChecksum(figureChangeResponse, 13); + m_figure_added_removed_responses.push(figureChangeResponse); + + return id; +} + +void DimensionsToypad::RemoveFigure(u8 pad, u8 index, bool fullRemove) { + std::lock_guard lock(m_dimensions_mutex); + + DimensionsFigure& figure = GetFigureByIndex(index); + if (figure.index == 255) + return; + + // When a figure is removed from the toypad, respond to the game with the pad they were removed + // from, their index, the direction (0x01 in byte 6 for removed) and their UID + if (fullRemove) { + std::array figureChangeResponse = { + 0x56, 0x0b, figure.pad, 0x00, figure.index, + 0x01, figure.data[0], figure.data[1], figure.data[2], figure.data[4], + figure.data[5], figure.data[6], figure.data[7]}; + figureChangeResponse[13] = GenerateChecksum(figureChangeResponse, 13); + m_figure_added_removed_responses.push(figureChangeResponse); + figure.Save(); + figure.dimFile.Close(); + } + + figure.index = 255; + figure.pad = 255; + figure.id = 0; +} + +void DimensionsToypad::MoveFigure(u8 new_pad, u8 new_index, u8 old_pad, u8 old_index) { + if (old_index == new_index) { + // Don't bother removing and loading again, just send response to the game + CancelRemoveFigure(new_index); + return; + } + + // When moving figures between spaces on the toypad, remove any figure from the space they are + // moving to, then remove them from their current space, then load them to the space they are + // moving to + RemoveFigure(new_pad, new_index, true); + + DimensionsFigure& figure = GetFigureByIndex(old_index); + const std::array data = figure.data; + Common::FS::IOFile inFile = std::move(figure.dimFile); + + RemoveFigure(old_pad, old_index, false); + + LoadDimensionsFigure(data, std::move(inFile), new_pad, new_index); +} + +void DimensionsToypad::TempRemoveFigure(u8 index) { + std::lock_guard lock(m_dimensions_mutex); + + DimensionsFigure& figure = GetFigureByIndex(index); + if (figure.index == 255) + return; + + // Send a response to the game that the figure has been "Picked up" from existing slot, + // until either the movement is cancelled, or user chooses a space to move to + std::array figureChangeResponse = { + 0x56, 0x0b, figure.pad, 0x00, figure.index, + 0x01, figure.data[0], figure.data[1], figure.data[2], figure.data[4], + figure.data[5], figure.data[6], figure.data[7]}; + + figureChangeResponse[13] = GenerateChecksum(figureChangeResponse, 13); + m_figure_added_removed_responses.push(figureChangeResponse); +} + +void DimensionsToypad::CancelRemoveFigure(u8 index) { + std::lock_guard lock(m_dimensions_mutex); + + DimensionsFigure& figure = GetFigureByIndex(index); + if (figure.index == 255) + return; + + // Cancel the previous movement of the figure + std::array figureChangeResponse = { + 0x56, 0x0b, figure.pad, 0x00, figure.index, + 0x00, figure.data[0], figure.data[1], figure.data[2], figure.data[4], + figure.data[5], figure.data[6], figure.data[7]}; + + figureChangeResponse[13] = GenerateChecksum(figureChangeResponse, 13); + m_figure_added_removed_responses.push(figureChangeResponse); +} + +u8 DimensionsToypad::GenerateChecksum(const std::array& data, u32 num_of_bytes) { + int checksum = 0; + ASSERT(num_of_bytes <= data.size()); + for (u8 i = 0; i < num_of_bytes; i++) { + checksum += data[i]; + } + return (checksum & 0xFF); +} + +void DimensionsToypad::GetBlankResponse(u8 type, u8 sequence, std::array& reply_buf) { + reply_buf[0] = 0x55; + reply_buf[1] = type; + reply_buf[2] = sequence; + reply_buf[3] = GenerateChecksum(reply_buf, 3); +} + +void DimensionsToypad::GenerateRandomNumber(const u8* buf, u8 sequence, + std::array& reply_buf) { + // Decrypt payload into an 8 byte array + const std::array value = Decrypt(buf, std::nullopt); + // Seed is the first 4 bytes (little endian) of the decrypted payload + const u32 seed = (u32&)value[0]; + // Confirmation is the second 4 bytes (big endian) of the decrypted payload + // const u32 conf = (u32be&)value[4]; + // Initialize rng using the seed from decrypted payload + InitializeRNG(seed); + // Encrypt 8 bytes, first 4 bytes is the decrypted confirmation from payload, 2nd 4 bytes are + // blank + std::array value_to_encrypt = {value[4], value[5], value[6], value[7], 0, 0, 0, 0}; + const std::array encrypted = Encrypt(value_to_encrypt.data(), std::nullopt); + reply_buf[0] = 0x55; + reply_buf[1] = 0x09; + reply_buf[2] = sequence; + // Copy encrypted value to response data + std::memcpy(&reply_buf[3], encrypted.data(), encrypted.size()); + reply_buf[11] = GenerateChecksum(reply_buf, 11); +} + +void DimensionsToypad::InitializeRNG(u32 seed) { + m_random_a = 0xF1EA5EED; + m_random_b = seed; + m_random_c = seed; + m_random_d = seed; + + for (int i = 0; i < 42; i++) { + GetNext(); + } +} + +u32 DimensionsToypad::GetNext() { + const u32 e = m_random_a - std::rotl(m_random_b, 21); + m_random_a = m_random_b ^ std::rotl(m_random_c, 19); + m_random_b = m_random_c + std::rotl(m_random_d, 6); + m_random_c = m_random_d + e; + m_random_d = e + m_random_a; + return m_random_d; +} + +std::array DimensionsToypad::Decrypt(const u8* buf, std::optional> key) { + // Value to decrypt is separated in to two little endian 32 bit unsigned integers + u32 data_one = u32(buf[0]) | (u32(buf[1]) << 8) | (u32(buf[2]) << 16) | (u32(buf[3]) << 24); + u32 data_two = u32(buf[4]) | (u32(buf[5]) << 8) | (u32(buf[6]) << 16) | (u32(buf[7]) << 24); + + // Use the key as 4 32 bit little endian unsigned integers + u32 key_one; + u32 key_two; + u32 key_three; + u32 key_four; + + if (key) { + key_one = u32(key.value()[0]) | (u32(key.value()[1]) << 8) | (u32(key.value()[2]) << 16) | + (u32(key.value()[3]) << 24); + key_two = u32(key.value()[4]) | (u32(key.value()[5]) << 8) | (u32(key.value()[6]) << 16) | + (u32(key.value()[7]) << 24); + key_three = u32(key.value()[8]) | (u32(key.value()[9]) << 8) | + (u32(key.value()[10]) << 16) | (u32(key.value()[11]) << 24); + key_four = u32(key.value()[12]) | (u32(key.value()[13]) << 8) | + (u32(key.value()[14]) << 16) | (u32(key.value()[15]) << 24); + } else { + key_one = u32(COMMAND_KEY[0]) | (u32(COMMAND_KEY[1]) << 8) | (u32(COMMAND_KEY[2]) << 16) | + (u32(COMMAND_KEY[3]) << 24); + key_two = u32(COMMAND_KEY[4]) | (u32(COMMAND_KEY[5]) << 8) | (u32(COMMAND_KEY[6]) << 16) | + (u32(COMMAND_KEY[7]) << 24); + key_three = u32(COMMAND_KEY[8]) | (u32(COMMAND_KEY[9]) << 8) | + (u32(COMMAND_KEY[10]) << 16) | (u32(COMMAND_KEY[11]) << 24); + key_four = u32(COMMAND_KEY[12]) | (u32(COMMAND_KEY[13]) << 8) | + (u32(COMMAND_KEY[14]) << 16) | (u32(COMMAND_KEY[15]) << 24); + } + + u32 sum = 0xC6EF3720; + constexpr u32 delta = 0x9E3779B9; + + for (int i = 0; i < 32; i++) { + data_two -= + (((data_one << 4) + key_three) ^ (data_one + sum) ^ ((data_one >> 5) + key_four)); + data_one -= (((data_two << 4) + key_one) ^ (data_two + sum) ^ ((data_two >> 5) + key_two)); + sum -= delta; + } + + ASSERT_MSG(sum == 0, "Decryption failed, sum inequal to 0"); + + std::array decrypted = {u8(data_one & 0xFF), u8((data_one >> 8) & 0xFF), + u8((data_one >> 16) & 0xFF), u8((data_one >> 24) & 0xFF), + u8(data_two & 0xFF), u8((data_two >> 8) & 0xFF), + u8((data_two >> 16) & 0xFF), u8((data_two >> 24) & 0xFF)}; + return decrypted; +} + +std::array DimensionsToypad::Encrypt(const u8* buf, std::optional> key) { + // Value to encrypt is separated in to two little endian 32 bit unsigned integers + u32 data_one = u32(buf[0]) | (u32(buf[1]) << 8) | (u32(buf[2]) << 16) | (u32(buf[3]) << 24); + u32 data_two = u32(buf[4]) | (u32(buf[5]) << 8) | (u32(buf[6]) << 16) | (u32(buf[7]) << 24); + + // Use the key as 4 32 bit little endian unsigned integers + u32 key_one; + u32 key_two; + u32 key_three; + u32 key_four; + + if (key) { + key_one = u32(key.value()[0]) | (u32(key.value()[1]) << 8) | (u32(key.value()[2]) << 16) | + (u32(key.value()[3]) << 24); + key_two = u32(key.value()[4]) | (u32(key.value()[5]) << 8) | (u32(key.value()[6]) << 16) | + (u32(key.value()[7]) << 24); + key_three = u32(key.value()[8]) | (u32(key.value()[9]) << 8) | + (u32(key.value()[10]) << 16) | (u32(key.value()[11]) << 24); + key_four = u32(key.value()[12]) | (u32(key.value()[13]) << 8) | + (u32(key.value()[14]) << 16) | (u32(key.value()[15]) << 24); + } else { + key_one = u32(COMMAND_KEY[0]) | (u32(COMMAND_KEY[1]) << 8) | (u32(COMMAND_KEY[2]) << 16) | + (u32(COMMAND_KEY[3]) << 24); + key_two = u32(COMMAND_KEY[4]) | (u32(COMMAND_KEY[5]) << 8) | (u32(COMMAND_KEY[6]) << 16) | + (u32(COMMAND_KEY[7]) << 24); + key_three = u32(COMMAND_KEY[8]) | (u32(COMMAND_KEY[9]) << 8) | + (u32(COMMAND_KEY[10]) << 16) | (u32(COMMAND_KEY[11]) << 24); + key_four = u32(COMMAND_KEY[12]) | (u32(COMMAND_KEY[13]) << 8) | + (u32(COMMAND_KEY[14]) << 16) | (u32(COMMAND_KEY[15]) << 24); + } + + u32 sum = 0; + u32 delta = 0x9E3779B9; + + for (int i = 0; i < 32; i++) { + sum += delta; + data_one += (((data_two << 4) + key_one) ^ (data_two + sum) ^ ((data_two >> 5) + key_two)); + data_two += + (((data_one << 4) + key_three) ^ (data_one + sum) ^ ((data_one >> 5) + key_four)); + } + + std::array encrypted = {u8(data_one & 0xFF), u8((data_one >> 8) & 0xFF), + u8((data_one >> 16) & 0xFF), u8((data_one >> 24) & 0xFF), + u8(data_two & 0xFF), u8((data_two >> 8) & 0xFF), + u8((data_two >> 16) & 0xFF), u8((data_two >> 24) & 0xFF)}; + return encrypted; +} + +std::array DimensionsToypad::GenerateFigureKey(const std::array& buf) { + std::array uid = {buf[0], buf[1], buf[2], buf[4], buf[5], buf[6], buf[7]}; + + u32 scrambleA = Scramble(uid, 3); + u32 scrambleB = Scramble(uid, 4); + u32 scrambleC = Scramble(uid, 5); + u32 scrambleD = Scramble(uid, 6); + + return { + u8((scrambleA >> 24) & 0xFF), u8((scrambleA >> 16) & 0xFF), u8((scrambleA >> 8) & 0xFF), + u8(scrambleA & 0xFF), u8((scrambleB >> 24) & 0xFF), u8((scrambleB >> 16) & 0xFF), + u8((scrambleB >> 8) & 0xFF), u8(scrambleB & 0xFF), u8((scrambleC >> 24) & 0xFF), + u8((scrambleC >> 16) & 0xFF), u8((scrambleC >> 8) & 0xFF), u8(scrambleC & 0xFF), + u8((scrambleD >> 24) & 0xFF), u8((scrambleD >> 16) & 0xFF), u8((scrambleD >> 8) & 0xFF), + u8(scrambleD & 0xFF)}; +} + +u32 DimensionsToypad::Scramble(const std::array& uid, u8 count) { + std::vector to_scramble; + to_scramble.reserve(uid.size() + CHAR_CONSTANT.size()); + for (u8 x : uid) { + to_scramble.push_back(x); + } + for (u8 c : CHAR_CONSTANT) { + to_scramble.push_back(c); + } + to_scramble[(count * 4) - 1] = 0xaa; + + std::array randomized = DimensionsRandomize(to_scramble, count); + + return (u32(randomized[0]) << 24) | (u32(randomized[1]) << 16) | (u32(randomized[2]) << 8) | + u32(randomized[3]); +} + +std::array DimensionsToypad::PWDGenerate(const std::array& uid) { + std::vector pwdCalc = {PWD_CONSTANT.begin(), PWD_CONSTANT.end() - 1}; + for (u8 i = 0; i < uid.size(); i++) { + pwdCalc.insert(pwdCalc.begin() + i, uid[i]); + } + + return DimensionsRandomize(pwdCalc, 8); +} + +std::array DimensionsToypad::DimensionsRandomize(const std::vector& key, u8 count) { + u32 scrambled = 0; + for (u8 i = 0; i < count; i++) { + const u32 v4 = std::rotr(scrambled, 25); + const u32 v5 = std::rotr(scrambled, 10); + const u32 b = u32(key[i * 4]) | (u32(key[(i * 4) + 1]) << 8) | + (u32(key[(i * 4) + 2]) << 16) | (u32(key[(i * 4) + 3]) << 24); + scrambled = b + v4 + v5 - scrambled; + } + return {u8(scrambled & 0xFF), u8(scrambled >> 8 & 0xFF), u8(scrambled >> 16 & 0xFF), + u8(scrambled >> 24 & 0xFF)}; +} + +u32 DimensionsToypad::GetFigureId(const std::array& buf) { + const std::array figure_key = GenerateFigureKey(buf); + + const std::array decrypted = Decrypt(&buf[36 * 4], figure_key); + + const u32 fig_num = u32(decrypted[0]) | (u32(decrypted[1]) << 8) | (u32(decrypted[2]) << 16) | + (u32(decrypted[3]) << 24); + // Characters have their model number encrypted in page 36 + if (fig_num < 1000) { + return fig_num; + } + // Vehicles/Gadgets have their model number written as little endian in page 36 + return u32(buf[36 * 4]) | (u32(buf[(36 * 4) + 1]) << 8) | (u32(buf[(36 * 4) + 2]) << 16) | + (u32(buf[(36 * 4) + 3]) << 24); +} + +DimensionsFigure& DimensionsToypad::GetFigureByIndex(u8 index) { + return m_figures[index]; +} + +void DimensionsToypad::RandomUID(u8* uid_buffer) { + uid_buffer[0] = 0x04; + uid_buffer[7] = 0x80; + + for (u8 i = 1; i < 7; i++) { + u8 random = rand() % 255; + uid_buffer[i] = random; + } +} + +void DimensionsToypad::GetChallengeResponse(const u8* buf, u8 sequence, + std::array& reply_buf) { + // Decrypt payload into an 8 byte array + const std::array value = Decrypt(buf, std::nullopt); + // Confirmation is the first 4 bytes of the decrypted payload + // const u32 conf = read_from_ptr>(value); + // Generate next random number based on RNG + const u32 next_random = GetNext(); + // Encrypt an 8 byte array, first 4 bytes are the next random number (little endian) + // followed by the confirmation from the decrypted payload + std::array value_to_encrypt = {u8(next_random & 0xFF), + u8((next_random >> 8) & 0xFF), + u8((next_random >> 16) & 0xFF), + u8((next_random >> 24) & 0xFF), + value[0], + value[1], + value[2], + value[3]}; + const std::array encrypted = Encrypt(value_to_encrypt.data(), std::nullopt); + reply_buf[0] = 0x55; + reply_buf[1] = 0x09; + reply_buf[2] = sequence; + // Copy encrypted value to response data + std::memcpy(&reply_buf[3], encrypted.data(), encrypted.size()); + reply_buf[11] = GenerateChecksum(reply_buf, 11); +} + +void DimensionsToypad::QueryBlock(u8 index, u8 page, std::array& reply_buf, u8 sequence) { + std::lock_guard lock(m_dimensions_mutex); + + reply_buf[0] = 0x55; + reply_buf[1] = 0x12; + reply_buf[2] = sequence; + reply_buf[3] = 0x00; + + // Index from game begins at 1 rather than 0, so minus 1 here + if (const u8 figure_index = index - 1; figure_index < MAX_DIMENSIONS_FIGURES) { + const DimensionsFigure& figure = GetFigureByIndex(figure_index); + + // Query 4 pages of 4 bytes from the figure, copy this to the response + if (figure.index != 255 && (4 * page) < ((0x2D * 4) - 16)) { + std::memcpy(&reply_buf[4], figure.data.data() + (4 * page), 16); + } + } + reply_buf[20] = GenerateChecksum(reply_buf, 20); +} + +void DimensionsToypad::WriteBlock(u8 index, u8 page, const u8* to_write_buf, + std::array& reply_buf, u8 sequence) { + std::lock_guard lock(m_dimensions_mutex); + + reply_buf[0] = 0x55; + reply_buf[1] = 0x02; + reply_buf[2] = sequence; + reply_buf[3] = 0x00; + + // Index from game begins at 1 rather than 0, so minus 1 here + if (const u8 figure_index = index - 1; figure_index < MAX_DIMENSIONS_FIGURES) { + DimensionsFigure& figure = GetFigureByIndex(figure_index); + + // Copy 4 bytes to the page on the figure requested by the game + if (figure.index != 255 && page < 0x2D) { + // Id is written to page 36 + if (page == 36) { + figure.id = u32(to_write_buf[0]) | (u32(to_write_buf[1]) << 8) | + (u32(to_write_buf[2]) << 16) | (u32(to_write_buf[3]) << 24); + } + std::memcpy(figure.data.data() + (page * 4), to_write_buf, 4); + figure.Save(); + } + } + reply_buf[4] = GenerateChecksum(reply_buf, 4); +} + +void DimensionsToypad::GetModel(const u8* buf, u8 sequence, std::array& reply_buf) { + // Decrypt payload to 8 byte array, byte 1 is the index, 4-7 are the confirmation + const std::array value = Decrypt(buf, std::nullopt); + const u8 index = value[0]; + // const u32 conf = read_from_ptr>(value, 4); + std::array value_to_encrypt = {}; + // Response is the figure's id (little endian) followed by the confirmation from payload + // Index from game begins at 1 rather than 0, so minus 1 here + if (const u8 figure_index = index - 1; figure_index < MAX_DIMENSIONS_FIGURES) { + const DimensionsFigure& figure = GetFigureByIndex(figure_index); + value_to_encrypt = {u8(figure.id & 0xFF), + u8((figure.id >> 8) & 0xFF), + u8((figure.id >> 16) & 0xFF), + u8((figure.id >> 24) & 0xFF), + value[4], + value[5], + value[6], + value[7]}; + } + const std::array encrypted = Encrypt(value_to_encrypt.data(), std::nullopt); + reply_buf[0] = 0x55; + reply_buf[1] = 0x0a; + reply_buf[2] = sequence; + reply_buf[3] = 0x00; + // Copy encrypted message to response + std::memcpy(&reply_buf[4], encrypted.data(), encrypted.size()); + reply_buf[12] = GenerateChecksum(reply_buf, 12); +} + +std::optional> DimensionsToypad::PopAddedRemovedResponse() { + std::lock_guard lock(m_dimensions_mutex); + + if (m_figure_added_removed_responses.empty()) { + return std::nullopt; + } + + std::array response = m_figure_added_removed_responses.front(); + m_figure_added_removed_responses.pop(); + return response; +} + +libusb_endpoint_descriptor* DimensionsBackend::FillEndpointDescriptorPair() { + return m_endpoint_descriptors.data(); +} + +libusb_interface_descriptor* DimensionsBackend::FillInterfaceDescriptor( + libusb_endpoint_descriptor* descs) { + m_interface_descriptors[0].endpoint = descs; + return m_interface_descriptors.data(); +} + +libusb_config_descriptor* DimensionsBackend::FillConfigDescriptor(libusb_interface* inter) { + m_config_descriptors[0].interface = inter; + return m_config_descriptors.data(); +} + +libusb_device_descriptor* DimensionsBackend::FillDeviceDescriptor() { + return m_device_descriptors.data(); +} + +libusb_transfer_status DimensionsBackend::HandleAsyncTransfer(libusb_transfer* transfer) { + ASSERT(transfer->length == 32); + + switch (transfer->endpoint) { + case 0x81: { + // Read Endpoint, wait to respond with either an added/removed figure response, or a queued + // response from a previous write + bool responded = false; + while (!responded) { + std::lock_guard lock(m_query_mutex); + std::optional> response = + m_dimensions_toypad->PopAddedRemovedResponse(); + if (response) { + std::memcpy(transfer->buffer, response.value().data(), 0x20); + transfer->length = 32; + responded = true; + } else if (!m_queries.empty()) { + std::memcpy(transfer->buffer, m_queries.front().data(), 0x20); + transfer->length = 32; + m_queries.pop(); + responded = true; + } + } + break; + } + case 0x01: { + // Write endpoint, similar structure of request to the Infinity Base with a command for byte + // 3, sequence for byte 4, the payload after that, then a checksum for the final byte. + + const u8 command = transfer->buffer[2]; + const u8 sequence = transfer->buffer[3]; + + std::array q_result{}; + + switch (command) { + case 0xB0: // Wake + { + // Consistent device response to the wake command + q_result = {0x55, 0x0e, 0x01, 0x28, 0x63, 0x29, 0x20, 0x4c, 0x45, + 0x47, 0x4f, 0x20, 0x32, 0x30, 0x31, 0x34, 0x46}; + break; + } + case 0xB1: // Seed + { + // Initialise a random number generator using the seed provided + m_dimensions_toypad->GenerateRandomNumber(&transfer->buffer[4], sequence, q_result); + break; + } + case 0xB3: // Challenge + { + // Get the next number in the sequence based on the RNG from 0xB1 command + m_dimensions_toypad->GetChallengeResponse(&transfer->buffer[4], sequence, q_result); + break; + } + case 0xC0: // Color + case 0xC1: // Get Pad Color + case 0xC2: // Fade + case 0xC3: // Flash + case 0xC4: // Fade Random + case 0xC6: // Fade All + case 0xC7: // Flash All + case 0xC8: // Color All + { + // Send a blank response to acknowledge color has been sent to toypad + m_dimensions_toypad->GetBlankResponse(0x01, sequence, q_result); + break; + } + case 0xD2: // Read + { + // Read 4 pages from the figure at index (buf[4]), starting with page buf[5] + m_dimensions_toypad->QueryBlock(transfer->buffer[4], transfer->buffer[5], q_result, + sequence); + break; + } + case 0xD3: // Write + { + // Write 4 bytes to page buf[5] to the figure at index buf[4] + m_dimensions_toypad->WriteBlock(transfer->buffer[4], transfer->buffer[5], + &transfer->buffer[6], q_result, sequence); + break; + } + case 0xD4: // Model + { + // Get the model id of the figure at index buf[4] + m_dimensions_toypad->GetModel(&transfer->buffer[4], sequence, q_result); + break; + } + case 0xD0: // Tag List + case 0xE1: // PWD + case 0xE5: // Active + case 0xFF: // LEDS Query + { + // Further investigation required + LOG_ERROR(Lib_Usbd, "Unimplemented LD Function: {:x}", command); + break; + } + default: { + LOG_ERROR(Lib_Usbd, "Unknown LD Function: {:x}", command); + break; + } + } + std::lock_guard lock(m_query_mutex); + m_queries.push(q_result); + break; + } + default: + break; + } + return LIBUSB_TRANSFER_COMPLETED; +} + +s32 DimensionsBackend::SubmitTransfer(libusb_transfer* transfer) { + if (transfer->endpoint == 0x01) { + std::thread write_thread([this, transfer] { + HandleAsyncTransfer(transfer); + + const u8 flags = transfer->flags; + transfer->status = LIBUSB_TRANSFER_COMPLETED; + transfer->actual_length = transfer->length; + if (transfer->callback) { + transfer->callback(transfer); + } + if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) { + libusb_free_transfer(transfer); + } + }); + write_thread.detach(); + return LIBUSB_SUCCESS; + } + + return UsbEmulatedBackend::SubmitTransfer(transfer); +} + +void DimensionsFigure::Save() { + if (!dimFile.IsOpen()) + return; + + dimFile.Seek(0); + dimFile.Write(data); +} +} // namespace Libraries::Usbd \ No newline at end of file diff --git a/src/core/libraries/usbd/emulated/dimensions.h b/src/core/libraries/usbd/emulated/dimensions.h new file mode 100644 index 000000000..d9573b5f4 --- /dev/null +++ b/src/core/libraries/usbd/emulated/dimensions.h @@ -0,0 +1,118 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "common/io_file.h" +#include "core/libraries/usbd/usb_backend.h" + +namespace Libraries::Usbd { + +constexpr u8 DIMEMSIONS_BLOCK_COUNT = 0x2D; +constexpr u8 DIMENSIONS_BLOCK_SIZE = 0x04; +constexpr u8 DIMENSIONS_FIGURE_SIZE = DIMEMSIONS_BLOCK_COUNT * DIMENSIONS_BLOCK_SIZE; +constexpr u8 MAX_DIMENSIONS_FIGURES = 7; + +struct DimensionsFigure final { + Common::FS::IOFile dimFile; + std::array data{}; + u8 index = 255; + u8 pad = 255; + u32 id = 0; + void Save(); +}; + +class DimensionsToypad final : public UsbEmulatedImpl { +public: + DimensionsToypad(); + ~DimensionsToypad() override = default; + + static void GetBlankResponse(u8 type, u8 sequence, std::array& reply_buf); + void GenerateRandomNumber(const u8* buf, u8 sequence, std::array& reply_buf); + void InitializeRNG(u32 seed); + void GetChallengeResponse(const u8* buf, u8 sequence, std::array& reply_buf); + void QueryBlock(u8 index, u8 page, std::array& reply_buf, u8 sequence); + void WriteBlock(u8 index, u8 page, const u8* to_write_buf, std::array& reply_buf, + u8 sequence); + void GetModel(const u8* buf, u8 sequence, std::array& reply_buf); + std::optional> PopAddedRemovedResponse(); + + void LoadFigure(std::string file_name, u8 pad, u8 slot) override; + void RemoveFigure(u8 pad, u8 slot, bool full_remove) override; + void MoveFigure(u8 new_pad, u8 new_index, u8 old_pad, u8 old_index) override; + void TempRemoveFigure(u8 index) override; + void CancelRemoveFigure(u8 index) override; + + u32 LoadDimensionsFigure(const std::array& buf, Common::FS::IOFile file, + u8 pad, u8 index); + +protected: + std::mutex m_dimensions_mutex; + std::array m_figures{}; + +private: + static void RandomUID(u8* uid_buffer); + static u8 GenerateChecksum(const std::array& data, u32 num_of_bytes); + static std::array Decrypt(const u8* buf, std::optional> key); + static std::array Encrypt(const u8* buf, std::optional> key); + static std::array GenerateFigureKey(const std::array& buf); + static u32 Scramble(const std::array& uid, u8 count); + static std::array PWDGenerate(const std::array& uid); + static std::array DimensionsRandomize(const std::vector& key, u8 count); + static u32 GetFigureId(const std::array& buf); + u32 GetNext(); + DimensionsFigure& GetFigureByIndex(u8 index); + + u32 m_random_a{}; + u32 m_random_b{}; + u32 m_random_c{}; + u32 m_random_d{}; + + u8 m_figure_order = 0; + std::queue> m_figure_added_removed_responses; +}; + +class DimensionsBackend final : public UsbEmulatedBackend { +protected: + libusb_endpoint_descriptor* FillEndpointDescriptorPair() override; + libusb_interface_descriptor* FillInterfaceDescriptor( + libusb_endpoint_descriptor* descs) override; + libusb_config_descriptor* FillConfigDescriptor(libusb_interface* inter) override; + libusb_device_descriptor* FillDeviceDescriptor() override; + + s32 GetMaxPacketSize(libusb_device* dev, u8 endpoint) override { + return 32; + } + + s32 SubmitTransfer(libusb_transfer* transfer) override; + + libusb_transfer_status HandleAsyncTransfer(libusb_transfer* transfer) override; + + std::shared_ptr GetImplRef() override { + return m_dimensions_toypad; + } + + std::mutex m_query_mutex; + std::queue> m_queries; + +private: + std::shared_ptr m_dimensions_toypad = std::make_shared(); + + std::array m_endpoint_out_extra = {0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x1d, 0x00}; + std::vector m_endpoint_descriptors = { + {0x7, 0x5, 0x81, 0x3, 0x20, 0x1, 0x0, 0x0}, {0x7, 0x5, 0x1, 0x3, 0x20, 0x1, 0x0, 0x0}}; + std::vector m_interface_descriptors = { + {0x9, 0x4, 0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0}}; + std::vector m_config_descriptors = { + {0x9, 0x2, 0x29, 0x1, 0x1, 0x0, 0x80, 0xFA}}; + std::vector m_device_descriptors = { + {0x12, 0x1, 0x200, 0x0, 0x0, 0x0, 0x20, 0x0E6F, 0x0241, 0x200, 0x1, 0x2, 0x0, 0x1}}; +}; +} // namespace Libraries::Usbd \ No newline at end of file diff --git a/src/core/libraries/usbd/usbd.h b/src/core/libraries/usbd/usbd.h index f189e0de2..fef9d6871 100644 --- a/src/core/libraries/usbd/usbd.h +++ b/src/core/libraries/usbd/usbd.h @@ -4,6 +4,7 @@ #pragma once #include "common/types.h" +#include "emulated/dimensions.h" #include "emulated/infinity.h" #include "emulated/skylander.h" #include "usb_backend.h" @@ -34,10 +35,9 @@ using SceUsbdTransfer = libusb_transfer; using SceUsbdControlSetup = libusb_control_setup; using SceUsbdTransferCallback = void PS4_SYSV_ABI (*)(SceUsbdTransfer* transfer); -// TODO: implement emulated devices using SkylandersPortalBackend = SkylanderBackend; using InfinityBaseBackend = InfinityBackend; -using DimensionsToypadBackend = UsbRealBackend; +using DimensionsToypadBackend = DimensionsBackend; enum class SceUsbdSpeed : u32 { UNKNOWN = 0, From 2f022a462ddd28829c68b5753348535555e49616 Mon Sep 17 00:00:00 2001 From: Emma Date: Thu, 6 Nov 2025 20:47:40 +0000 Subject: [PATCH 09/25] filesystem: return st_mtim in posix_stat (fixes RB4 / CUSA02901 DLC crash) (#3781) * filesystem: return st_mtim in posix_stat * filesystem: stat - remove reliance on clock_cast * filesystem: stat - remove reliance on to_time_t --- src/core/libraries/kernel/file_system.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index d5cbe6074..7ded1f33e 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -640,17 +640,29 @@ s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { *__Error() = POSIX_ENOENT; return -1; } + + // get the difference between file clock and system clock + const auto now_sys = std::chrono::system_clock::now(); + const auto now_file = std::filesystem::file_time_type::clock::now(); + // calculate the file modified time + const auto mtime = std::filesystem::last_write_time(path_name); + const auto mtimestamp = now_sys + (mtime - now_file); + if (std::filesystem::is_directory(path_name)) { sb->st_mode = 0000777u | 0040000u; sb->st_size = 65536; sb->st_blksize = 65536; sb->st_blocks = 128; + sb->st_mtim.tv_sec = + std::chrono::duration_cast(mtimestamp.time_since_epoch()).count(); // TODO incomplete } else { sb->st_mode = 0000777u | 0100000u; sb->st_size = static_cast(std::filesystem::file_size(path_name)); sb->st_blksize = 512; sb->st_blocks = (sb->st_size + 511) / 512; + sb->st_mtim.tv_sec = + std::chrono::duration_cast(mtimestamp.time_since_epoch()).count(); // TODO incomplete } From b4628b80e23e85e114c0ddfc39bd277d8656694b Mon Sep 17 00:00:00 2001 From: Emma Date: Thu, 6 Nov 2025 23:58:15 +0000 Subject: [PATCH 10/25] ImGui: keep drawing when there's a pending change_layer (#3782) --- src/imgui/renderer/imgui_core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index f82612444..452dee013 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -250,7 +250,7 @@ void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view, } bool MustKeepDrawing() { - return layers.size() > 1 || DebugState.IsShowingDebugMenuBar(); + return layers.size() > 1 || change_layers.size() > 1 || DebugState.IsShowingDebugMenuBar(); } } // namespace Core From 3ce1ac5e86f4ef2c3c1d75fba499cc75c2587f85 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 7 Nov 2025 08:59:26 +0200 Subject: [PATCH 11/25] tagged 0.12.5 release --- CMakeLists.txt | 6 +++--- dist/net.shadps4.shadPS4.metainfo.xml | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 95475d0c1..e67080fa5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,12 +203,12 @@ execute_process( # Set Version set(EMULATOR_VERSION_MAJOR "0") set(EMULATOR_VERSION_MINOR "12") -set(EMULATOR_VERSION_PATCH "1") +set(EMULATOR_VERSION_PATCH "5") set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}") -set(APP_VERSION "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_VERSION_PATCH} WIP") -set(APP_IS_RELEASE false) +set(APP_VERSION "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_VERSION_PATCH}") +set(APP_IS_RELEASE true) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp" @ONLY) message("-- end git things, remote: ${GIT_REMOTE_NAME}, branch: ${GIT_BRANCH}, link: ${GIT_REMOTE_URL}") diff --git a/dist/net.shadps4.shadPS4.metainfo.xml b/dist/net.shadps4.shadPS4.metainfo.xml index ae07e79d8..5798876f6 100644 --- a/dist/net.shadps4.shadPS4.metainfo.xml +++ b/dist/net.shadps4.shadPS4.metainfo.xml @@ -37,6 +37,9 @@ Game + + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.12.5 + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.12.0 From 5ddabda2b8fd5bcba6a123974fb3291b6f772bde Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 7 Nov 2025 09:16:58 +0200 Subject: [PATCH 12/25] started 0.12.6 WIP --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e67080fa5..d26581790 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,12 +203,12 @@ execute_process( # Set Version set(EMULATOR_VERSION_MAJOR "0") set(EMULATOR_VERSION_MINOR "12") -set(EMULATOR_VERSION_PATCH "5") +set(EMULATOR_VERSION_PATCH "6") set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}") -set(APP_VERSION "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_VERSION_PATCH}") -set(APP_IS_RELEASE true) +set(APP_VERSION "${EMULATOR_VERSION_MAJOR}.${EMULATOR_VERSION_MINOR}.${EMULATOR_VERSION_PATCH} WIP") +set(APP_IS_RELEASE false) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp" @ONLY) message("-- end git things, remote: ${GIT_REMOTE_NAME}, branch: ${GIT_BRANCH}, link: ${GIT_REMOTE_URL}") From bebfee58d6e362198221da1f0615406590c615e5 Mon Sep 17 00:00:00 2001 From: Connor Garey Date: Sun, 9 Nov 2025 03:08:18 +0000 Subject: [PATCH 13/25] Nix shell fixes for uuid (#3784) * added "with pkgs;" so pkgs does not need to be appended for all the buildInputs. * Added util linux as missing uuid. Compiles successfully. --- shell.nix | 65 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/shell.nix b/shell.nix index 50336d1b2..611095268 100644 --- a/shell.nix +++ b/shell.nix @@ -6,42 +6,43 @@ with import (fetchTarball "https://github.com/nixos/nixpkgs/archive/cfd19cdc5468 pkgs.mkShell { name = "shadps4-build-env"; - nativeBuildInputs = [ - pkgs.llvmPackages_18.clang - pkgs.cmake - pkgs.pkg-config - pkgs.git + nativeBuildInputs = with pkgs; [ + llvmPackages_18.clang + cmake + pkg-config + git + util-linux ]; - buildInputs = [ - pkgs.alsa-lib - pkgs.libpulseaudio - pkgs.openal - pkgs.zlib - pkgs.libedit - pkgs.udev - pkgs.libevdev - pkgs.SDL2 - pkgs.jack2 - pkgs.sndio + buildInputs = with pkgs; [ + alsa-lib + libpulseaudio + openal + zlib + libedit + udev + libevdev + SDL2 + jack2 + sndio - pkgs.vulkan-headers - pkgs.vulkan-utility-libraries - pkgs.vulkan-tools + vulkan-headers + vulkan-utility-libraries + vulkan-tools - pkgs.ffmpeg - pkgs.fmt - pkgs.glslang - pkgs.libxkbcommon - pkgs.wayland - pkgs.xorg.libxcb - pkgs.xorg.xcbutil - pkgs.xorg.xcbutilkeysyms - pkgs.xorg.xcbutilwm - pkgs.sdl3 - pkgs.stb - pkgs.wayland-protocols - pkgs.libpng + ffmpeg + fmt + glslang + libxkbcommon + wayland + xorg.libxcb + xorg.xcbutil + xorg.xcbutilkeysyms + xorg.xcbutilwm + sdl3 + stb + wayland-protocols + libpng ]; shellHook = '' From ee2bc97248f460670e982f7966a70c092c896676 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Mon, 10 Nov 2025 09:07:17 -0600 Subject: [PATCH 14/25] Windows: Limit address space maximum when higher addresses are not needed (#3775) * Earlier initialization of elf info. Everything used for elf info initialization comes from the param.sfo, so we can initialize this earlier to have this information accessible during memory init. * Extract compiled SDK version from pubtoolinfo string Up until now, we've been using the game's reported "firmware version" as our compiled SDK version. This behavior is inaccurate, and is something that has come up in my hardware tests before. For the actual compiled SDK version, we should use the SDK version in the PUBTOOLINFO string of the param.sfo, only falling back on the firmware version when that the sdk_ver component isn't present. * Store compiled SDK version in ElfInfo * Limit address space for compiled SDK version at or above FW 3 Sony placed a hard cap at 0xfc00000000, with a slight extension for stack mappings. For now, though stack mappings aren't implemented, there's no harm in keeping a slightly extended address space (since this cap is lower than our old user max). Limiting the max through address space is necessary for Windows due to performance issues, in the future I plan to properly implement checks in memory manager code to properly handle this behavior for all platforms. * Use compiled SDK version for sceKernelGetCompiledSdkVersion I think this is pretty self explanatory. * Log SDK version Since this value is what most internal firmware version checks are against, logging the value will help with debugging. * Update address_space.cpp * Update emulator.cpp * Backwards compatible logging Because that's apparently an issue now --- src/common/elf_info.h | 6 +++ src/core/address_space.cpp | 25 ++++++++---- src/core/libraries/kernel/process.cpp | 2 +- src/emulator.cpp | 58 +++++++++++++++++++-------- 4 files changed, 66 insertions(+), 25 deletions(-) diff --git a/src/common/elf_info.h b/src/common/elf_info.h index 2f2f150b2..0b2589e95 100644 --- a/src/common/elf_info.h +++ b/src/common/elf_info.h @@ -68,6 +68,7 @@ class ElfInfo { std::string app_ver{}; u32 firmware_ver = 0; u32 raw_firmware_ver = 0; + u32 sdk_ver = 0; PSFAttributes psf_attributes{}; std::filesystem::path splash_path{}; @@ -117,6 +118,11 @@ public: return raw_firmware_ver; } + [[nodiscard]] u32 CompiledSdkVer() const { + ASSERT(initialized); + return sdk_ver; + } + [[nodiscard]] const PSFAttributes& GetPSFAttributes() const { ASSERT(initialized); return psf_attributes; diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp index 3f2d94cbf..3f063ea76 100644 --- a/src/core/address_space.cpp +++ b/src/core/address_space.cpp @@ -6,6 +6,7 @@ #include "common/arch.h" #include "common/assert.h" #include "common/config.h" +#include "common/elf_info.h" #include "common/error.h" #include "core/address_space.h" #include "core/libraries/kernel/memory.h" @@ -103,8 +104,8 @@ struct AddressSpace::Impl { GetSystemInfo(&sys_info); u64 alignment = sys_info.dwAllocationGranularity; - // Determine the host OS build number - // Retrieve module handle for ntdll + // Older Windows builds have a severe performance issue with VirtualAlloc2. + // We need to get the host's Windows version, then determine if it needs a workaround. auto ntdll_handle = GetModuleHandleW(L"ntdll.dll"); ASSERT_MSG(ntdll_handle, "Failed to retrieve ntdll handle"); @@ -120,12 +121,20 @@ struct AddressSpace::Impl { u64 supported_user_max = USER_MAX; // This is the build number for Windows 11 22H2 static constexpr s32 AffectedBuildNumber = 22621; - if (os_version_info.dwBuildNumber <= AffectedBuildNumber) { - // Older Windows builds have an issue with VirtualAlloc2 on higher addresses. - // To prevent regressions, limit the maximum address we reserve for this platform. - supported_user_max = 0x11000000000ULL; - LOG_WARNING(Core, "Windows 10 detected, reducing user max to {:#x} to avoid problems", - supported_user_max); + + // Higher PS4 firmware versions prevent higher address mappings too. + s32 sdk_ver = Common::ElfInfo::Instance().CompiledSdkVer(); + if (os_version_info.dwBuildNumber <= AffectedBuildNumber || + sdk_ver >= Common::ElfInfo::FW_30) { + supported_user_max = 0x10000000000ULL; + // Only log the message if we're restricting the user max due to operating system. + // Since higher compiled SDK versions also get reduced max, we don't need to log there. + if (sdk_ver < Common::ElfInfo::FW_30) { + LOG_WARNING( + Core, + "Older Windows version detected, reducing user max to {:#x} to avoid problems", + supported_user_max); + } } // Determine the free address ranges we can access. diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index 8220e55d8..02da041c3 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -36,7 +36,7 @@ s32 PS4_SYSV_ABI sceKernelGetMainSocId() { } s32 PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(s32* ver) { - s32 version = Common::ElfInfo::Instance().RawFirmwareVer(); + s32 version = Common::ElfInfo::Instance().CompiledSdkVer(); *ver = version; return (version >= 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL; } diff --git a/src/emulator.cpp b/src/emulator.cpp index 0e7485b89..ad407f9b6 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -113,6 +113,7 @@ void Emulator::Run(std::filesystem::path file, std::vector args, std::string id; std::string title; std::string app_version; + u32 sdk_version; u32 fw_version; Common::PSFAttributes psf_attributes{}; if (param_sfo_exists) { @@ -132,8 +133,48 @@ void Emulator::Run(std::filesystem::path file, std::vector args, if (const auto raw_attributes = param_sfo->GetInteger("ATTRIBUTE")) { psf_attributes.raw = *raw_attributes; } + + // Extract sdk version from pubtool info. + std::string_view pubtool_info = + param_sfo->GetString("PUBTOOLINFO").value_or("Unknown value"); + u64 sdk_ver_offset = pubtool_info.find("sdk_ver"); + + if (sdk_ver_offset == pubtool_info.npos) { + // Default to using firmware version if SDK version is not found. + sdk_version = fw_version; + } else { + // Increment offset to account for sdk_ver= part of string. + sdk_ver_offset += 8; + u64 sdk_ver_len = pubtool_info.find(",", sdk_ver_offset); + if (sdk_ver_len == pubtool_info.npos) { + // If there's no more commas, this is likely the last entry of pubtool info. + // Use string length instead. + sdk_ver_len = pubtool_info.size(); + } + sdk_ver_len -= sdk_ver_offset; + std::string sdk_ver_string = pubtool_info.substr(sdk_ver_offset, sdk_ver_len).data(); + // Number is stored in base 16. + sdk_version = std::stoi(sdk_ver_string, nullptr, 16); + } } + auto& game_info = Common::ElfInfo::Instance(); + game_info.initialized = true; + game_info.game_serial = id; + game_info.title = title; + game_info.app_ver = app_version; + game_info.firmware_ver = fw_version & 0xFFF00000; + game_info.raw_firmware_ver = fw_version; + game_info.sdk_ver = sdk_version; + game_info.psf_attributes = psf_attributes; + + const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png"); + if (std::filesystem::exists(pic1_path)) { + game_info.splash_path = pic1_path; + } + + game_info.game_folder = game_folder; + Config::load(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (id + ".toml"), true); @@ -196,6 +237,7 @@ void Emulator::Run(std::filesystem::path file, std::vector args, if (param_sfo_exists) { LOG_INFO(Loader, "Game id: {} Title: {}", id, title); LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version); + LOG_INFO(Loader, "Compiled SDK version: {:#x}", sdk_version); LOG_INFO(Loader, "PSVR Supported: {}", (bool)psf_attributes.support_ps_vr.Value()); LOG_INFO(Loader, "PSVR Required: {}", (bool)psf_attributes.require_ps_vr.Value()); } @@ -235,22 +277,6 @@ void Emulator::Run(std::filesystem::path file, std::vector args, } } - auto& game_info = Common::ElfInfo::Instance(); - game_info.initialized = true; - game_info.game_serial = id; - game_info.title = title; - game_info.app_ver = app_version; - game_info.firmware_ver = fw_version & 0xFFF00000; - game_info.raw_firmware_ver = fw_version; - game_info.psf_attributes = psf_attributes; - - const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png"); - if (std::filesystem::exists(pic1_path)) { - game_info.splash_path = pic1_path; - } - - game_info.game_folder = game_folder; - std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version); std::string window_title = ""; std::string remote_url(Common::g_scm_remote_url); From bbd985fe4b771c2f24d8d05a9dc3b43cd5f50f00 Mon Sep 17 00:00:00 2001 From: Missake Date: Tue, 11 Nov 2025 16:56:03 +0100 Subject: [PATCH 15/25] Update building-windows.md (#3792) --- documents/building-windows.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/documents/building-windows.md b/documents/building-windows.md index aa7213abc..d1f8a6895 100644 --- a/documents/building-windows.md +++ b/documents/building-windows.md @@ -36,9 +36,8 @@ Go through the Git for Windows installation as normal 1. Open up Visual Studio, select `Open a local folder` and select the folder with the shadPS4 source code. The folder should contain `CMakeLists.txt` 2. Change Clang x64 Debug to Clang x64 Release if you want a regular, non-debug build. -3. If you want to build shadPS4 with the Qt Gui, simply select Clang x64 Release with Qt instead. -4. Change the project to build to shadps4.exe -5. Build -> Build All +3. Change the project to build to shadps4.exe +4. Build -> Build All Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\` From 25344a3b89207b5bd9f6e530654191e4cdb55aa8 Mon Sep 17 00:00:00 2001 From: Joshua de Reeper Date: Wed, 12 Nov 2025 11:40:39 +0100 Subject: [PATCH 16/25] calloc UsbDevice instead of pointer (#3790) --- src/core/libraries/usbd/usb_backend.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/usbd/usb_backend.h b/src/core/libraries/usbd/usb_backend.h index 7a5c2899a..505f825ab 100644 --- a/src/core/libraries/usbd/usb_backend.h +++ b/src/core/libraries/usbd/usb_backend.h @@ -366,7 +366,7 @@ public: const auto desc = FillDeviceDescriptor(); ASSERT(desc); - const auto fake = static_cast(calloc(1, sizeof(UsbDevice*))); + const auto fake = static_cast(calloc(1, sizeof(UsbDevice))); fake->bus_number = 0; fake->port_number = 0; fake->device_address = 0; From 93c340c6e1804ad167b4cd9f61ce5912277c8281 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:26:26 -0600 Subject: [PATCH 17/25] calloc libusb_interface instead of pointer (#3793) interface->num_altsetting is oob --- src/core/libraries/usbd/usb_backend.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/libraries/usbd/usb_backend.h b/src/core/libraries/usbd/usb_backend.h index 505f825ab..e668d5509 100644 --- a/src/core/libraries/usbd/usb_backend.h +++ b/src/core/libraries/usbd/usb_backend.h @@ -312,7 +312,7 @@ public: const auto endpoint_descs = FillEndpointDescriptorPair(); const auto interface_desc = FillInterfaceDescriptor(endpoint_descs); - const auto interface = static_cast(calloc(1, sizeof(libusb_interface*))); + const auto interface = static_cast(calloc(1, sizeof(libusb_interface))); interface->altsetting = interface_desc; interface->num_altsetting = 1; From f557c6ac645337feb898b8174aff7a7e6f03c03e Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Fri, 14 Nov 2025 20:47:16 +0100 Subject: [PATCH 18/25] Update MoltenVK to the shadPS4 fork (#3797) --- .gitmodules | 2 +- externals/MoltenVK | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 38aed89a0..43ba2a7c1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -108,7 +108,7 @@ branch = dist [submodule "externals/MoltenVK"] path = externals/MoltenVK - url = https://github.com/KhronosGroup/MoltenVK.git + url = https://github.com/shadPS4-emu/ext-MoltenVK.git shallow = true [submodule "externals/json"] path = externals/json diff --git a/externals/MoltenVK b/externals/MoltenVK index b23d42534..f168dec05 160000 --- a/externals/MoltenVK +++ b/externals/MoltenVK @@ -1 +1 @@ -Subproject commit b23d42534622cd9926fe526fec1b7f8795a2853c +Subproject commit f168dec05998ab0ca09a400bab6831a95c0bdb2e From 94d0f2e7edade6e9e96d38aed1f38f1d43247e6f Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Fri, 14 Nov 2025 21:50:14 -0600 Subject: [PATCH 19/25] Avoid initializing Shader::PsColorBuffer in RefreshGraphicsKey (#3799) The bitfield in the struct is padded, which produces uninitialized memory on initialization. To avoid modifying the struct while making our GraphicsPipelineKey struct properly hashable, set values directly instead of re-initializing. This fixes pipeline compile spam, and the subsequent poor performance, on certain setups. --- .../renderer_vulkan/vk_pipeline_cache.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index be9543737..4706bff24 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -355,13 +355,12 @@ bool PipelineCache::RefreshGraphicsKey() { } // Fill color target information - key.color_buffers[cb] = Shader::PsColorBuffer{ - .data_format = col_buf.GetDataFmt(), - .num_format = col_buf.GetNumberFmt(), - .num_conversion = col_buf.GetNumberConversion(), - .export_format = regs.color_export_format.GetFormat(cb), - .swizzle = col_buf.Swizzle(), - }; + auto& color_buffer = key.color_buffers[cb]; + color_buffer.data_format = col_buf.GetDataFmt(); + color_buffer.num_format = col_buf.GetNumberFmt(); + color_buffer.num_conversion = col_buf.GetNumberConversion(); + color_buffer.export_format = regs.color_export_format.GetFormat(cb); + color_buffer.swizzle = col_buf.Swizzle(); } // Compile and bind shader stages @@ -379,7 +378,7 @@ bool PipelineCache::RefreshGraphicsKey() { continue; } if ((key.mrt_mask & (1u << cb)) == 0) { - key.color_buffers[cb] = {}; + std::memset(&key.color_buffers[cb], 0, sizeof(Shader::PsColorBuffer)); continue; } From 2f556366268ec9273364521afd53afa68f07cb99 Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Sat, 15 Nov 2025 07:44:25 +0200 Subject: [PATCH 20/25] vk_rasterizer: Attempt to optimize compute clears (#3795) --- src/video_core/buffer_cache/buffer_cache.cpp | 2 +- .../renderer_vulkan/vk_rasterizer.cpp | 63 ++++++++++++++++++- .../renderer_vulkan/vk_rasterizer.h | 1 + 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 04c473f1b..cb18bc190 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -889,7 +889,7 @@ bool BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, }); TouchBuffer(buffer); } - if (is_texel_buffer) { + if (is_texel_buffer && !is_written) { return SynchronizeBufferFromImage(buffer, device_addr, size); } return false; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a47c523e1..37b8051e8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -380,7 +380,8 @@ void Rasterizer::OnSubmit() { } bool Rasterizer::BindResources(const Pipeline* pipeline) { - if (IsComputeImageCopy(pipeline) || IsComputeMetaClear(pipeline)) { + if (IsComputeImageCopy(pipeline) || IsComputeMetaClear(pipeline) || + IsComputeImageClear(pipeline)) { return false; } @@ -520,6 +521,66 @@ bool Rasterizer::IsComputeImageCopy(const Pipeline* pipeline) { return true; } +bool Rasterizer::IsComputeImageClear(const Pipeline* pipeline) { + if (!pipeline->IsCompute()) { + return false; + } + + // Ensure shader only has 2 bound buffers + const auto& cs_pgm = liverpool->GetCsRegs(); + const auto& info = pipeline->GetStage(Shader::LogicalStage::Compute); + if (cs_pgm.num_thread_x.full != 64 || info.buffers.size() != 2 || !info.images.empty()) { + return false; + } + + // From those 2 buffers, first must hold the clear vector and second the image being cleared + const auto& desc0 = info.buffers[0]; + const auto& desc1 = info.buffers[1]; + if (desc0.is_formatted || !desc1.is_formatted || desc0.is_written || !desc1.is_written) { + return false; + } + + // First buffer must have size of vec4 and second the size of a single layer + const AmdGpu::Buffer buf0 = desc0.GetSharp(info); + const AmdGpu::Buffer buf1 = desc1.GetSharp(info); + const u32 buf1_bpp = AmdGpu::NumBitsPerBlock(buf1.GetDataFmt()); + if (buf0.GetSize() != 16 || (cs_pgm.dim_x * 128ULL * (buf1_bpp / 8)) != buf1.GetSize()) { + return false; + } + + // Find image the buffer alias + const auto image1_id = + texture_cache.FindImageFromRange(buf1.base_address, buf1.GetSize(), false); + if (!image1_id) { + return false; + } + + // Image clear must be valid + VideoCore::Image& image1 = texture_cache.GetImage(image1_id); + if (image1.info.guest_size != buf1.GetSize() || image1.info.num_bits != buf1_bpp || + image1.info.props.is_depth) { + return false; + } + + // Perform image clear + const float* values = reinterpret_cast(buf0.base_address); + const vk::ClearValue clear = { + .color = {.float32 = std::array{values[0], values[1], values[2], values[3]}}, + }; + const VideoCore::SubresourceRange range = { + .base = + { + .level = 0, + .layer = 0, + }, + .extent = image1.info.resources, + }; + image1.Clear(clear, range); + image1.flags |= VideoCore::ImageFlagBits::GpuModified; + image1.flags &= ~VideoCore::ImageFlagBits::Dirty; + return true; +} + void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Bindings& binding, Shader::PushData& push_data) { buffer_bindings.clear(); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 524a8f06d..96a3c95e8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -112,6 +112,7 @@ private: bool IsComputeMetaClear(const Pipeline* pipeline); bool IsComputeImageCopy(const Pipeline* pipeline); + bool IsComputeImageClear(const Pipeline* pipeline); private: friend class VideoCore::BufferCache; From 6a9f9abda09029edd8c2ae37a7a550b1033fce9d Mon Sep 17 00:00:00 2001 From: Missake Date: Sat, 15 Nov 2025 06:48:04 +0100 Subject: [PATCH 21/25] Delete lines about Qt in building doc for Windows (#3800) --- documents/building-windows.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/documents/building-windows.md b/documents/building-windows.md index d1f8a6895..88c5b6830 100644 --- a/documents/building-windows.md +++ b/documents/building-windows.md @@ -41,10 +41,6 @@ Go through the Git for Windows installation as normal Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\` -To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal: -`C:\Qt\\msvc2022_64\bin\windeployqt6.exe "C:\path\to\shadps4.exe"` -(Change Qt path if you've installed it to non-default path) - ## Option 2: MSYS2/MinGW > [!IMPORTANT] @@ -72,7 +68,6 @@ ARM64-based computers, follow: 1. Open "MSYS2 CLANGARM64" from your new applications 2. Run `pacman -Syu`, let it complete; 3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-rapidjson mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg` - 1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools mingw-w64-clang-aarch64-qt6-multimedia` 4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` 5. Run `cd shadPS4` 6. Run `cmake -S . -B build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"` From ed14359c87bc6ed9781d151cbb1f34b9fe045f4c Mon Sep 17 00:00:00 2001 From: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com> Date: Sun, 16 Nov 2025 16:36:54 +0800 Subject: [PATCH 22/25] Re-implement custom trophy sounds using sdl3 mixer (#3805) * re-implement custom trophy sounds using sdl3 mixer * fix build vars * Don't change SDL version --- .gitmodules | 4 ++ CMakeLists.txt | 3 +- externals/CMakeLists.txt | 12 +++++ externals/sdl3_mixer | 1 + src/core/libraries/np/trophy_ui.cpp | 73 ++++++++++++----------------- src/core/libraries/np/trophy_ui.h | 4 ++ 6 files changed, 52 insertions(+), 45 deletions(-) create mode 160000 externals/sdl3_mixer diff --git a/.gitmodules b/.gitmodules index 43ba2a7c1..b8d1544e4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -113,3 +113,7 @@ [submodule "externals/json"] path = externals/json url = https://github.com/nlohmann/json.git +[submodule "externals/sdl3_mixer"] + path = externals/sdl3_mixer + url = https://github.com/libsdl-org/SDL_mixer + shallow = true diff --git a/CMakeLists.txt b/CMakeLists.txt index d26581790..1491a3e1e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -229,6 +229,7 @@ find_package(magic_enum 0.9.7 CONFIG) find_package(PNG 1.6 MODULE) find_package(RenderDoc 1.6.0 MODULE) find_package(SDL3 3.1.2 CONFIG) +find_package(SDL3_mixer 2.8.1 CONFIG) find_package(stb MODULE) find_package(toml11 4.2.0 CONFIG) find_package(tsl-robin-map 1.3.0 CONFIG) @@ -1070,7 +1071,7 @@ add_executable(shadps4 create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) -target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json) +target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 SDL3_mixer::SDL3_mixer pugixml::pugixml stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index a26f58d69..a45a03bf6 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -63,6 +63,18 @@ if (NOT TARGET SDL3::SDL3) add_subdirectory(sdl3) endif() +# SDL3_mixer +if (NOT TARGET SDL3_Mixer::SDL3_Mixer) + set(SDLMIXER_FLAC OFF) + set(SDLMIXER_OGG OFF) + set(SDLMIXER_MOD OFF) + set(SDLMIXER_MIDI OFF) + set(SDLMIXER_OPUS OFF) + set(SDLMIXER_WAVPACK OFF) + set(BUILD_SHARED_LIBS OFF) + add_subdirectory(sdl3_mixer) +endif() + # vulkan-headers if (NOT TARGET Vulkan::Headers) set(VULKAN_HEADERS_ENABLE_MODULE OFF) diff --git a/externals/sdl3_mixer b/externals/sdl3_mixer new file mode 160000 index 000000000..4182794ea --- /dev/null +++ b/externals/sdl3_mixer @@ -0,0 +1 @@ +Subproject commit 4182794ea45fe28568728670c6f1583855d0e85c diff --git a/src/core/libraries/np/trophy_ui.cpp b/src/core/libraries/np/trophy_ui.cpp index 94e7eb5f2..b803403c4 100644 --- a/src/core/libraries/np/trophy_ui.cpp +++ b/src/core/libraries/np/trophy_ui.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -92,59 +91,45 @@ TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::strin AddLayer(this); - bool customsoundplayed = false; -#ifdef ENABLE_QT_GUI - QString musicPathWav = QString::fromStdString(CustomTrophy_Dir.string() + "/trophy.wav"); - QString musicPathMp3 = QString::fromStdString(CustomTrophy_Dir.string() + "/trophy.mp3"); - if (fs::exists(musicPathWav.toStdString())) { - BackgroundMusicPlayer::getInstance().setVolume(100); - BackgroundMusicPlayer::getInstance().playMusic(musicPathWav, false); - customsoundplayed = true; - } else if (fs::exists(musicPathMp3.toStdString())) { - BackgroundMusicPlayer::getInstance().setVolume(100); - BackgroundMusicPlayer::getInstance().playMusic(musicPathMp3, false); - customsoundplayed = true; + MIX_Init(); + mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL); + if (!mixer) { + LOG_ERROR(Lib_NpTrophy, "Could not initialize SDL Mixer, {}", SDL_GetError()); + return; } -#endif - if (!customsoundplayed) { + MIX_SetMasterGain(mixer, static_cast(Config::getVolumeSlider() / 100.f)); + auto musicPathMp3 = CustomTrophy_Dir / "trophy.mp3"; + auto musicPathWav = CustomTrophy_Dir / "trophy.wav"; + + if (std::filesystem::exists(musicPathMp3)) { + audio = MIX_LoadAudio(mixer, musicPathMp3.string().c_str(), false); + } else if (std::filesystem::exists(musicPathWav)) { + audio = MIX_LoadAudio(mixer, musicPathWav.string().c_str(), false); + } else { auto soundFile = resource.open("src/images/trophy.wav"); std::vector soundData = std::vector(soundFile.begin(), soundFile.end()); + audio = + MIX_LoadAudio_IO(mixer, SDL_IOFromMem(soundData.data(), soundData.size()), false, true); + // due to low volume of default sound file + MIX_SetMasterGain(mixer, MIX_GetMasterGain(mixer) * 1.3f); + } - SDL_AudioSpec spec; - Uint8* audioBuf; - Uint32 audioLen; + if (!audio) { + LOG_ERROR(Lib_NpTrophy, "Could not loud audio file, {}", SDL_GetError()); + return; + } - if (!SDL_LoadWAV_IO(SDL_IOFromMem(soundData.data(), soundData.size()), true, &spec, - &audioBuf, &audioLen)) { - LOG_ERROR(Lib_NpTrophy, "Cannot load trophy sound: {}", SDL_GetError()); - SDL_free(audioBuf); - return; - } - - SDL_AudioStream* stream = - SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, nullptr, nullptr); - if (!stream) { - LOG_ERROR(Lib_NpTrophy, "Cannot create audio stream for trophy sound: {}", - SDL_GetError()); - SDL_free(audioBuf); - return; - } - - if (!SDL_PutAudioStreamData(stream, audioBuf, audioLen)) { - LOG_ERROR(Lib_NpTrophy, "Cannot add trophy sound data to stream: {}", SDL_GetError()); - SDL_free(audioBuf); - return; - } - - // Set audio gain 20% higher since audio file itself is soft - SDL_SetAudioStreamGain(stream, Config::getVolumeSlider() / 100.0f * 1.2f); - SDL_ResumeAudioStreamDevice(stream); - SDL_free(audioBuf); + if (!MIX_PlayAudio(mixer, audio)) { + LOG_ERROR(Lib_NpTrophy, "Could not play audio file, {}", SDL_GetError()); } } TrophyUI::~TrophyUI() { + MIX_DestroyAudio(audio); + MIX_DestroyMixer(mixer); + MIX_Quit(); + Finish(); } diff --git a/src/core/libraries/np/trophy_ui.h b/src/core/libraries/np/trophy_ui.h index fbadac8f4..2734471b3 100644 --- a/src/core/libraries/np/trophy_ui.h +++ b/src/core/libraries/np/trophy_ui.h @@ -5,6 +5,7 @@ #include #include +#include #include #include "common/fixed_value.h" @@ -30,6 +31,9 @@ private: std::string_view trophy_type; ImGui::RefCountedTexture trophy_icon; ImGui::RefCountedTexture trophy_type_icon; + + MIX_Mixer* mixer; + MIX_Audio* audio; }; struct TrophyInfo { From aa5c045555ebd211855121064fdf53e08430cb35 Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Sun, 16 Nov 2025 17:34:23 +0200 Subject: [PATCH 23/25] logging: Format message after filter check (#3808) --- src/common/logging/backend.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 6b68651de..4a85c4cde 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -182,7 +182,13 @@ public: } void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, - const char* function, std::string message) { + const char* function, const char* format, const fmt::format_args& args) { + if (!filter.CheckMessage(log_class, log_level) || !Config::getLoggingEnabled()) { + return; + } + + const auto message = fmt::vformat(format, args); + // Propagate important log messages to the profiler if (IsProfilerConnected()) { const auto& msg_str = fmt::format("[{}] {}", GetLogClassName(log_class), message); @@ -201,10 +207,6 @@ public: } } - if (!filter.CheckMessage(log_class, log_level) || !Config::getLoggingEnabled()) { - return; - } - using std::chrono::duration_cast; using std::chrono::microseconds; using std::chrono::steady_clock; @@ -324,8 +326,8 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, const char* format, const fmt::format_args& args) { if (!initialization_in_progress_suppress_logging) [[likely]] { - Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, - fmt::vformat(format, args)); + Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, format, + args); } } } // namespace Common::Log From 5b699090e6bb5936f9d2d809ee6309e3de12868a Mon Sep 17 00:00:00 2001 From: Alexandre Bouvier Date: Mon, 17 Nov 2025 16:56:01 +0000 Subject: [PATCH 24/25] cmake: fix sdl3_mixer target name (#3811) --- externals/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index a45a03bf6..5f7ae94c4 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -64,7 +64,7 @@ if (NOT TARGET SDL3::SDL3) endif() # SDL3_mixer -if (NOT TARGET SDL3_Mixer::SDL3_Mixer) +if (NOT TARGET SDL3_mixer::SDL3_mixer) set(SDLMIXER_FLAC OFF) set(SDLMIXER_OGG OFF) set(SDLMIXER_MOD OFF) From 3f86c2e94ad645d8a26b676b026b39fb1f2b8071 Mon Sep 17 00:00:00 2001 From: TheTurtle Date: Tue, 18 Nov 2025 08:46:51 +0200 Subject: [PATCH 25/25] buffer_cache: Split DMA fault handling code from buffer cache (#3809) Its better not to have that raw code there --- CMakeLists.txt | 2 + .../ir/passes/shader_info_collection_pass.cpp | 2 +- src/video_core/buffer_cache/buffer_cache.cpp | 238 +----------------- src/video_core/buffer_cache/buffer_cache.h | 21 +- src/video_core/buffer_cache/fault_manager.cpp | 177 +++++++++++++ src/video_core/buffer_cache/fault_manager.h | 42 ++++ .../host_shaders/fault_buffer_process.comp | 27 +- .../renderer_vulkan/vk_rasterizer.cpp | 13 +- .../renderer_vulkan/vk_scheduler.cpp | 17 +- 9 files changed, 249 insertions(+), 290 deletions(-) create mode 100644 src/video_core/buffer_cache/fault_manager.cpp create mode 100644 src/video_core/buffer_cache/fault_manager.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1491a3e1e..ddaf2422c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -960,6 +960,8 @@ set(VIDEO_CORE src/video_core/amdgpu/cb_db_extent.h src/video_core/buffer_cache/buffer.h src/video_core/buffer_cache/buffer_cache.cpp src/video_core/buffer_cache/buffer_cache.h + src/video_core/buffer_cache/fault_manager.cpp + src/video_core/buffer_cache/fault_manager.h src/video_core/buffer_cache/memory_tracker.h src/video_core/buffer_cache/range_set.h src/video_core/buffer_cache/region_definitions.h diff --git a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp index 3df4f8b86..c298a1092 100644 --- a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp +++ b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -190,7 +190,7 @@ void CollectShaderInfoPass(IR::Program& program, const Profile& profile) { }); info.buffers.push_back({ .used_types = IR::Type::U32, - .inline_cbuf = AmdGpu::Buffer::Placeholder(VideoCore::BufferCache::FAULT_BUFFER_SIZE), + .inline_cbuf = AmdGpu::Buffer::Placeholder(std::numeric_limits::max()), .buffer_type = BufferType::FaultBuffer, .is_written = true, }); diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index cb18bc190..ac3fac5b1 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -2,49 +2,42 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include #include "common/alignment.h" #include "common/debug.h" #include "common/scope_exit.h" -#include "common/types.h" #include "core/memory.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/buffer_cache/memory_tracker.h" -#include "video_core/host_shaders/fault_buffer_process_comp.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" #include "video_core/renderer_vulkan/vk_instance.h" -#include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_scheduler.h" -#include "video_core/renderer_vulkan/vk_shader_util.h" #include "video_core/texture_cache/texture_cache.h" namespace VideoCore { static constexpr size_t DataShareBufferSize = 64_KB; static constexpr size_t StagingBufferSize = 512_MB; +static constexpr size_t DownloadBufferSize = 32_MB; static constexpr size_t UboStreamBufferSize = 64_MB; -static constexpr size_t DownloadBufferSize = 128_MB; static constexpr size_t DeviceBufferSize = 128_MB; -static constexpr size_t MaxPageFaults = 1024; BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, AmdGpu::Liverpool* liverpool_, TextureCache& texture_cache_, PageManager& tracker) : instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, memory{Core::Memory::Instance()}, texture_cache{texture_cache_}, + fault_manager{instance, scheduler, *this, CACHING_PAGEBITS, CACHING_NUMPAGES}, staging_buffer{instance, scheduler, MemoryUsage::Upload, StagingBufferSize}, stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize}, download_buffer{instance, scheduler, MemoryUsage::Download, DownloadBufferSize}, device_buffer{instance, scheduler, MemoryUsage::DeviceLocal, DeviceBufferSize}, gds_buffer{instance, scheduler, MemoryUsage::Stream, 0, AllFlags, DataShareBufferSize}, bda_pagetable_buffer{instance, scheduler, MemoryUsage::DeviceLocal, - 0, AllFlags, BDA_PAGETABLE_SIZE}, - fault_buffer(instance, scheduler, MemoryUsage::DeviceLocal, 0, AllFlags, FAULT_BUFFER_SIZE) { + 0, AllFlags, BDA_PAGETABLE_SIZE} { Vulkan::SetObjectName(instance.GetDevice(), gds_buffer.Handle(), "GDS Buffer"); Vulkan::SetObjectName(instance.GetDevice(), bda_pagetable_buffer.Handle(), "BDA Page Table Buffer"); - Vulkan::SetObjectName(instance.GetDevice(), fault_buffer.Handle(), "Fault Buffer"); memory_tracker = std::make_unique(tracker); @@ -57,80 +50,6 @@ BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& s const vk::Buffer& null_buffer = slot_buffers[null_id].buffer; Vulkan::SetObjectName(instance.GetDevice(), null_buffer, "Null Buffer"); - // Prepare the fault buffer parsing pipeline - boost::container::static_vector bindings{ - { - .binding = 0, - .descriptorType = vk::DescriptorType::eStorageBuffer, - .descriptorCount = 1, - .stageFlags = vk::ShaderStageFlagBits::eCompute, - }, - { - .binding = 1, - .descriptorType = vk::DescriptorType::eStorageBuffer, - .descriptorCount = 1, - .stageFlags = vk::ShaderStageFlagBits::eCompute, - }, - }; - - const vk::DescriptorSetLayoutCreateInfo desc_layout_ci = { - .flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR, - .bindingCount = static_cast(bindings.size()), - .pBindings = bindings.data(), - }; - auto [desc_layout_result, desc_layout] = - instance.GetDevice().createDescriptorSetLayoutUnique(desc_layout_ci); - ASSERT_MSG(desc_layout_result == vk::Result::eSuccess, - "Failed to create descriptor set layout: {}", vk::to_string(desc_layout_result)); - fault_process_desc_layout = std::move(desc_layout); - - const auto& module = Vulkan::Compile(HostShaders::FAULT_BUFFER_PROCESS_COMP, - vk::ShaderStageFlagBits::eCompute, instance.GetDevice()); - Vulkan::SetObjectName(instance.GetDevice(), module, "Fault Buffer Parser"); - - const vk::SpecializationMapEntry specialization_map_entry = { - .constantID = 0, - .offset = 0, - .size = sizeof(u32), - }; - - const vk::SpecializationInfo specialization_info = { - .mapEntryCount = 1, - .pMapEntries = &specialization_map_entry, - .dataSize = sizeof(u32), - .pData = &CACHING_PAGEBITS, - }; - - const vk::PipelineShaderStageCreateInfo shader_ci = { - .stage = vk::ShaderStageFlagBits::eCompute, - .module = module, - .pName = "main", - .pSpecializationInfo = &specialization_info, - }; - - const vk::PipelineLayoutCreateInfo layout_info = { - .setLayoutCount = 1U, - .pSetLayouts = &(*fault_process_desc_layout), - }; - auto [layout_result, layout] = instance.GetDevice().createPipelineLayoutUnique(layout_info); - ASSERT_MSG(layout_result == vk::Result::eSuccess, "Failed to create pipeline layout: {}", - vk::to_string(layout_result)); - fault_process_pipeline_layout = std::move(layout); - - const vk::ComputePipelineCreateInfo pipeline_info = { - .stage = shader_ci, - .layout = *fault_process_pipeline_layout, - }; - auto [pipeline_result, pipeline] = - instance.GetDevice().createComputePipelineUnique({}, pipeline_info); - ASSERT_MSG(pipeline_result == vk::Result::eSuccess, "Failed to create compute pipeline: {}", - vk::to_string(pipeline_result)); - fault_process_pipeline = std::move(pipeline); - Vulkan::SetObjectName(instance.GetDevice(), *fault_process_pipeline, - "Fault Buffer Parser Pipeline"); - - instance.GetDevice().destroyShaderModule(module); - // Set up garbage collection parameters if (!instance.CanReportMemoryUsage()) { trigger_gc_memory = DEFAULT_TRIGGER_GC_MEMORY; @@ -656,14 +575,10 @@ BufferId BufferCache::CreateBuffer(VAddr device_addr, u32 wanted_size) { wanted_size = static_cast(device_addr_end - device_addr); const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size); const u32 size = static_cast(overlap.end - overlap.begin); - const BufferId new_buffer_id = [&] { - std::scoped_lock lk{slot_buffers_mutex}; - return slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin, - AllFlags | vk::BufferUsageFlagBits::eShaderDeviceAddress, size); - }(); + const BufferId new_buffer_id = + slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin, + AllFlags | vk::BufferUsageFlagBits::eShaderDeviceAddress, size); auto& new_buffer = slot_buffers[new_buffer_id]; - const size_t size_bytes = new_buffer.SizeBytes(); - const auto cmdbuf = scheduler.CommandBuffer(); for (const BufferId overlap_id : overlap.ids) { JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); } @@ -672,126 +587,7 @@ BufferId BufferCache::CreateBuffer(VAddr device_addr, u32 wanted_size) { } void BufferCache::ProcessFaultBuffer() { - // Run fault processing shader - const auto [mapped, offset] = download_buffer.Map(MaxPageFaults * sizeof(u64)); - vk::BufferMemoryBarrier2 fault_buffer_barrier{ - .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .srcAccessMask = vk::AccessFlagBits2::eShaderWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eComputeShader, - .dstAccessMask = vk::AccessFlagBits2::eShaderRead, - .buffer = fault_buffer.Handle(), - .offset = 0, - .size = FAULT_BUFFER_SIZE, - }; - vk::BufferMemoryBarrier2 download_barrier{ - .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eComputeShader, - .dstAccessMask = vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eShaderWrite, - .buffer = download_buffer.Handle(), - .offset = offset, - .size = MaxPageFaults * sizeof(u64), - }; - std::array barriers{fault_buffer_barrier, download_barrier}; - vk::DescriptorBufferInfo fault_buffer_info{ - .buffer = fault_buffer.Handle(), - .offset = 0, - .range = FAULT_BUFFER_SIZE, - }; - vk::DescriptorBufferInfo download_info{ - .buffer = download_buffer.Handle(), - .offset = offset, - .range = MaxPageFaults * sizeof(u64), - }; - boost::container::small_vector writes{ - { - .dstSet = VK_NULL_HANDLE, - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eStorageBuffer, - .pBufferInfo = &fault_buffer_info, - }, - { - .dstSet = VK_NULL_HANDLE, - .dstBinding = 1, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = vk::DescriptorType::eStorageBuffer, - .pBufferInfo = &download_info, - }, - }; - download_buffer.Commit(); - scheduler.EndRendering(); - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.fillBuffer(download_buffer.Handle(), offset, MaxPageFaults * sizeof(u64), 0); - cmdbuf.pipelineBarrier2(vk::DependencyInfo{ - .dependencyFlags = vk::DependencyFlagBits::eByRegion, - .bufferMemoryBarrierCount = 2, - .pBufferMemoryBarriers = barriers.data(), - }); - cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, *fault_process_pipeline); - cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, *fault_process_pipeline_layout, 0, - writes); - constexpr u32 num_threads = CACHING_NUMPAGES / 32; // 1 bit per page, 32 pages per workgroup - constexpr u32 num_workgroups = Common::DivCeil(num_threads, 64u); - cmdbuf.dispatch(num_workgroups, 1, 1); - - // Reset fault buffer - const vk::BufferMemoryBarrier2 reset_pre_barrier = { - .srcStageMask = vk::PipelineStageFlagBits2::eComputeShader, - .srcAccessMask = vk::AccessFlagBits2::eShaderRead, - .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, - .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, - .buffer = fault_buffer.Handle(), - .offset = 0, - .size = FAULT_BUFFER_SIZE, - }; - const vk::BufferMemoryBarrier2 reset_post_barrier = { - .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .dstAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite, - .buffer = fault_buffer.Handle(), - .offset = 0, - .size = FAULT_BUFFER_SIZE, - }; - cmdbuf.pipelineBarrier2(vk::DependencyInfo{ - .dependencyFlags = vk::DependencyFlagBits::eByRegion, - .bufferMemoryBarrierCount = 1, - .pBufferMemoryBarriers = &reset_pre_barrier, - }); - cmdbuf.fillBuffer(fault_buffer.buffer, 0, FAULT_BUFFER_SIZE, 0); - cmdbuf.pipelineBarrier2(vk::DependencyInfo{ - .dependencyFlags = vk::DependencyFlagBits::eByRegion, - .bufferMemoryBarrierCount = 1, - .pBufferMemoryBarriers = &reset_post_barrier, - }); - - // Defer creating buffers - scheduler.DeferOperation([this, mapped]() { - // Create the fault buffers batched - boost::icl::interval_set fault_ranges; - const u64* fault_ptr = std::bit_cast(mapped); - const u32 fault_count = static_cast(*(fault_ptr++)); - for (u32 i = 0; i < fault_count; ++i) { - const VAddr fault = *(fault_ptr++); - const VAddr fault_end = fault + CACHING_PAGESIZE; // This can be adjusted - fault_ranges += - boost::icl::interval_set::interval_type::right_open(fault, fault_end); - LOG_INFO(Render_Vulkan, "Accessed non-GPU cached memory at {:#x}", fault); - } - for (const auto& range : fault_ranges) { - const VAddr start = range.lower(); - const VAddr end = range.upper(); - const u64 page_start = start >> CACHING_PAGEBITS; - const u64 page_end = Common::DivCeil(end, CACHING_PAGESIZE); - // Buffer size is in 32 bits - ASSERT_MSG((range.upper() - range.lower()) <= std::numeric_limits::max(), - "Buffer size is too large"); - CreateBuffer(start, static_cast(end - start)); - } - }); + fault_manager.ProcessFaultBuffer(); } void BufferCache::Register(BufferId buffer_id) { @@ -972,10 +768,7 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, } void BufferCache::SynchronizeBuffersInRange(VAddr device_addr, u64 size) { - if (device_addr == 0) { - return; - } - VAddr device_addr_end = device_addr + size; + const VAddr device_addr_end = device_addr + size; ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) { RENDERER_TRACE; VAddr start = std::max(buffer.CpuAddr(), device_addr); @@ -985,21 +778,6 @@ void BufferCache::SynchronizeBuffersInRange(VAddr device_addr, u64 size) { }); } -void BufferCache::MemoryBarrier() { - scheduler.EndRendering(); - const auto cmdbuf = scheduler.CommandBuffer(); - vk::MemoryBarrier2 barrier = { - .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eMemoryWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .dstAccessMask = vk::AccessFlagBits2::eMemoryRead, - }; - cmdbuf.pipelineBarrier2(vk::DependencyInfo{ - .memoryBarrierCount = 1, - .pMemoryBarriers = &barrier, - }); -} - void BufferCache::InlineDataBuffer(Buffer& buffer, VAddr address, const void* value, u32 num_bytes) { scheduler.EndRendering(); diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index ccf77b4f5..6954f979e 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -3,12 +3,12 @@ #pragma once -#include #include #include "common/lru_cache.h" #include "common/slot_vector.h" #include "common/types.h" #include "video_core/buffer_cache/buffer.h" +#include "video_core/buffer_cache/fault_manager.h" #include "video_core/buffer_cache/range_set.h" #include "video_core/multi_level_page_table.h" @@ -40,9 +40,7 @@ public: static constexpr u64 CACHING_PAGESIZE = u64{1} << CACHING_PAGEBITS; static constexpr u64 DEVICE_PAGESIZE = 16_KB; static constexpr u64 CACHING_NUMPAGES = u64{1} << (40 - CACHING_PAGEBITS); - static constexpr u64 BDA_PAGETABLE_SIZE = CACHING_NUMPAGES * sizeof(vk::DeviceAddress); - static constexpr u64 FAULT_BUFFER_SIZE = CACHING_NUMPAGES / 8; // Bit per page // Default values for garbage collection static constexpr s64 DEFAULT_TRIGGER_GC_MEMORY = 1_GB; @@ -68,12 +66,6 @@ public: bool has_stream_leap = false; }; - using IntervalSet = - boost::icl::interval_set; - using IntervalType = typename IntervalSet::interval_type; - public: explicit BufferCache(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, AmdGpu::Liverpool* liverpool, TextureCache& texture_cache, @@ -92,7 +84,7 @@ public: /// Retrieves the fault buffer. [[nodiscard]] Buffer* GetFaultBuffer() noexcept { - return &fault_buffer; + return fault_manager.GetFaultBuffer(); } /// Retrieves the buffer with the specified id. @@ -160,9 +152,6 @@ public: /// Synchronizes all buffers neede for DMA. void SynchronizeDmaBuffers(); - /// Record memory barrier. Used for buffers when accessed via BDA. - void MemoryBarrier(); - /// Runs the garbage collector. void RunGarbageCollector(); @@ -217,6 +206,7 @@ private: AmdGpu::Liverpool* liverpool; Core::MemoryManager* memory; TextureCache& texture_cache; + FaultManager fault_manager; std::unique_ptr memory_tracker; StreamBuffer staging_buffer; StreamBuffer stream_buffer; @@ -224,8 +214,6 @@ private: StreamBuffer device_buffer; Buffer gds_buffer; Buffer bda_pagetable_buffer; - Buffer fault_buffer; - std::shared_mutex slot_buffers_mutex; Common::SlotVector slot_buffers; u64 total_used_memory = 0; u64 trigger_gc_memory = 0; @@ -235,9 +223,6 @@ private: RangeSet gpu_modified_ranges; SplitRangeMap buffer_ranges; PageTable page_table; - vk::UniqueDescriptorSetLayout fault_process_desc_layout; - vk::UniquePipeline fault_process_pipeline; - vk::UniquePipelineLayout fault_process_pipeline_layout; }; } // namespace VideoCore diff --git a/src/video_core/buffer_cache/fault_manager.cpp b/src/video_core/buffer_cache/fault_manager.cpp new file mode 100644 index 000000000..e967ffd0e --- /dev/null +++ b/src/video_core/buffer_cache/fault_manager.cpp @@ -0,0 +1,177 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/div_ceil.h" +#include "video_core/buffer_cache/buffer_cache.h" +#include "video_core/buffer_cache/fault_manager.h" +#include "video_core/renderer_vulkan/vk_instance.h" +#include "video_core/renderer_vulkan/vk_platform.h" +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" + +#include "video_core/host_shaders/fault_buffer_process_comp.h" + +namespace VideoCore { + +static constexpr size_t MaxPageFaults = 1024; +static constexpr size_t PageFaultAreaSize = MaxPageFaults * sizeof(u64); + +FaultManager::FaultManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler_, + BufferCache& buffer_cache_, u32 caching_pagebits, u64 caching_num_pages_) + : scheduler{scheduler_}, buffer_cache{buffer_cache_}, + caching_pagesize{1ULL << caching_pagebits}, caching_num_pages{caching_num_pages_}, + fault_buffer_size{caching_num_pages_ / 8}, + fault_buffer{instance, scheduler, MemoryUsage::DeviceLocal, 0, AllFlags, fault_buffer_size}, + download_buffer{instance, scheduler, MemoryUsage::Download, + 0, AllFlags, MaxPendingFaults * PageFaultAreaSize} { + const auto device = instance.GetDevice(); + Vulkan::SetObjectName(device, fault_buffer.Handle(), "Fault Buffer"); + + const std::array bindings = {{ + { + .binding = 0, + .descriptorType = vk::DescriptorType::eStorageBuffer, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eCompute, + }, + { + .binding = 1, + .descriptorType = vk::DescriptorType::eStorageBuffer, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eCompute, + }, + }}; + const vk::DescriptorSetLayoutCreateInfo desc_layout_ci = { + .flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR, + .bindingCount = 2, + .pBindings = bindings.data(), + }; + fault_process_desc_layout = + Vulkan::Check(device.createDescriptorSetLayoutUnique(desc_layout_ci)); + + std::vector defines{{fmt::format("CACHING_PAGEBITS={}", caching_pagebits), + fmt::format("MAX_PAGE_FAULTS={}", MaxPageFaults)}}; + const auto module = Vulkan::Compile(HostShaders::FAULT_BUFFER_PROCESS_COMP, + vk::ShaderStageFlagBits::eCompute, device, defines); + Vulkan::SetObjectName(device, module, "Fault Buffer Parser"); + + const vk::PipelineShaderStageCreateInfo shader_ci = { + .stage = vk::ShaderStageFlagBits::eCompute, + .module = module, + .pName = "main", + }; + + const vk::PipelineLayoutCreateInfo layout_info = { + .setLayoutCount = 1U, + .pSetLayouts = &(*fault_process_desc_layout), + }; + fault_process_pipeline_layout = Vulkan::Check(device.createPipelineLayoutUnique(layout_info)); + + const vk::ComputePipelineCreateInfo pipeline_info = { + .stage = shader_ci, + .layout = *fault_process_pipeline_layout, + }; + fault_process_pipeline = Vulkan::Check(device.createComputePipelineUnique({}, pipeline_info)); + Vulkan::SetObjectName(device, *fault_process_pipeline, "Fault Buffer Parser Pipeline"); + + device.destroyShaderModule(module); +} + +void FaultManager::ProcessFaultBuffer() { + if (u64 wait_tick = fault_areas[current_area]) { + scheduler.Wait(wait_tick); + scheduler.PopPendingOperations(); + } + + const u32 offset = current_area * PageFaultAreaSize; + u8* mapped = download_buffer.mapped_data.data() + offset; + std::memset(mapped, 0, PageFaultAreaSize); + + const vk::BufferMemoryBarrier2 pre_barrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .srcAccessMask = vk::AccessFlagBits2::eShaderWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eComputeShader, + .dstAccessMask = vk::AccessFlagBits2::eShaderRead, + .buffer = fault_buffer.Handle(), + .offset = 0, + .size = fault_buffer_size, + }; + const vk::BufferMemoryBarrier2 post_barrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eComputeShader, + .srcAccessMask = vk::AccessFlagBits2::eShaderWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .dstAccessMask = vk::AccessFlagBits2::eShaderWrite, + .buffer = fault_buffer.Handle(), + .offset = 0, + .size = fault_buffer_size, + }; + const vk::DescriptorBufferInfo fault_buffer_info = { + .buffer = fault_buffer.Handle(), + .offset = 0, + .range = fault_buffer_size, + }; + const vk::DescriptorBufferInfo download_info = { + .buffer = download_buffer.Handle(), + .offset = offset, + .range = PageFaultAreaSize, + }; + const std::array writes = {{ + { + .dstSet = VK_NULL_HANDLE, + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eStorageBuffer, + .pBufferInfo = &fault_buffer_info, + }, + { + .dstSet = VK_NULL_HANDLE, + .dstBinding = 1, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eStorageBuffer, + .pBufferInfo = &download_info, + }, + }}; + scheduler.EndRendering(); + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = &pre_barrier, + }); + cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, *fault_process_pipeline); + cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, *fault_process_pipeline_layout, 0, + writes); + // 1 bit per page, 32 pages per workgroup + const u32 num_threads = caching_num_pages / 32; + const u32 num_workgroups = Common::DivCeil(num_threads, 64u); + cmdbuf.dispatch(num_workgroups, 1, 1); + + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = &post_barrier, + }); + + scheduler.DeferOperation([this, mapped, area = current_area] { + fault_ranges.Clear(); + const u64* fault_buf = std::bit_cast(mapped); + const u32 fault_count = fault_buf[0]; + for (u32 i = 1; i <= fault_count; ++i) { + fault_ranges.Add(fault_buf[i], caching_pagesize); + LOG_INFO(Render_Vulkan, "Accessed non-GPU cached memory at {:#x}", fault_buf[i]); + } + fault_ranges.ForEach([&](VAddr start, VAddr end) { + ASSERT_MSG((end - start) <= std::numeric_limits::max(), + "Buffer size is too large"); + buffer_cache.FindBuffer(start, static_cast(end - start)); + }); + fault_areas[area] = 0; + }); + + fault_areas[current_area++] = scheduler.CurrentTick(); + current_area %= MaxPendingFaults; +} + +} // namespace VideoCore diff --git a/src/video_core/buffer_cache/fault_manager.h b/src/video_core/buffer_cache/fault_manager.h new file mode 100644 index 000000000..4fd545433 --- /dev/null +++ b/src/video_core/buffer_cache/fault_manager.h @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/buffer_cache/buffer.h" +#include "video_core/buffer_cache/range_set.h" + +namespace VideoCore { + +class BufferCache; + +class FaultManager { + static constexpr size_t MaxPendingFaults = 8; + +public: + explicit FaultManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, + BufferCache& buffer_cache, u32 caching_pagebits, u64 caching_num_pages); + + [[nodiscard]] Buffer* GetFaultBuffer() noexcept { + return &fault_buffer; + } + + void ProcessFaultBuffer(); + +private: + Vulkan::Scheduler& scheduler; + BufferCache& buffer_cache; + RangeSet fault_ranges; + u64 caching_pagesize; + u64 caching_num_pages; + u64 fault_buffer_size; + Buffer fault_buffer; + Buffer download_buffer; + std::array fault_areas{}; + u32 current_area{}; + vk::UniqueDescriptorSetLayout fault_process_desc_layout; + vk::UniquePipeline fault_process_pipeline; + vk::UniquePipelineLayout fault_process_pipeline_layout; +}; + +} // namespace VideoCore diff --git a/src/video_core/host_shaders/fault_buffer_process.comp b/src/video_core/host_shaders/fault_buffer_process.comp index a712cf441..04a86bad3 100644 --- a/src/video_core/host_shaders/fault_buffer_process.comp +++ b/src/video_core/host_shaders/fault_buffer_process.comp @@ -13,30 +13,23 @@ layout(std430, binding = 0) buffer input_buf { layout(std430, binding = 1) buffer output_buf { uint64_t download_buffer[]; }; - -// Overlap for 32 bit atomics layout(std430, binding = 1) buffer output_buf32 { uint download_buffer32[]; }; -layout(constant_id = 0) const uint CACHING_PAGEBITS = 0; - void main() { - uint id = gl_GlobalInvocationID.x; + const uint id = gl_GlobalInvocationID.x; uint word = fault_buffer[id]; - if (word == 0u) { - return; - } - // 1 page per bit - uint base_bit = id * 32u; + fault_buffer[id] = 0u; + const uint base_bit = id * 32u; while (word != 0u) { - uint bit = findLSB(word); - word &= word - 1; - uint page = base_bit + bit; - uint store_index = atomicAdd(download_buffer32[0], 1u) + 1u; - // It is very unlikely, but should we check for overflow? - if (store_index < 1024u) { // only support 1024 page faults - download_buffer[store_index] = uint64_t(page) << CACHING_PAGEBITS; + const uint store_index = atomicAdd(download_buffer32[0], 1u) + 1u; + if (store_index >= MAX_PAGE_FAULTS) { + return; } + const uint bit = findLSB(word); + word &= word - 1; + const uint page = base_bit + bit; + download_buffer[store_index] = uint64_t(page) << CACHING_PAGEBITS; } } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 37b8051e8..8d00ff2d0 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -407,18 +407,13 @@ bool Rasterizer::BindResources(const Pipeline* pipeline) { if (uses_dma) { // We only use fault buffer for DMA right now. - { - Common::RecursiveSharedLock lock{mapped_ranges_mutex}; - for (auto& range : mapped_ranges) { - buffer_cache.SynchronizeBuffersInRange(range.lower(), - range.upper() - range.lower()); - } + Common::RecursiveSharedLock lock{mapped_ranges_mutex}; + for (auto& range : mapped_ranges) { + buffer_cache.SynchronizeBuffersInRange(range.lower(), range.upper() - range.lower()); } - buffer_cache.MemoryBarrier(); + fault_process_pending = true; } - fault_process_pending |= uses_dma; - return true; } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 78286957f..da7467dfb 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -84,15 +84,6 @@ void Scheduler::Wait(u64 tick) { Flush(info); } master_semaphore.Wait(tick); - - // CAUTION: This can introduce unexpected variation in the wait time. - // We don't currently sync the GPU, and some games are very sensitive to this. - // If this becomes a problem, it can be commented out. - // Idealy we would implement proper gpu sync. - while (!pending_ops.empty() && pending_ops.front().gpu_tick <= tick) { - pending_ops.front().callback(); - pending_ops.pop(); - } } void Scheduler::PopPendingOperations() { @@ -109,9 +100,7 @@ void Scheduler::AllocateWorkerCommandBuffers() { }; current_cmdbuf = command_pool.Commit(); - auto begin_result = current_cmdbuf.begin(begin_info); - ASSERT_MSG(begin_result == vk::Result::eSuccess, "Failed to begin command buffer: {}", - vk::to_string(begin_result)); + Check(current_cmdbuf.begin(begin_info)); // Invalidate dynamic state so it gets applied to the new command buffer. dynamic_state.Invalidate(); @@ -139,9 +128,7 @@ void Scheduler::SubmitExecution(SubmitInfo& info) { #endif EndRendering(); - auto end_result = current_cmdbuf.end(); - ASSERT_MSG(end_result == vk::Result::eSuccess, "Failed to end command buffer: {}", - vk::to_string(end_result)); + Check(current_cmdbuf.end()); const vk::Semaphore timeline = master_semaphore.Handle(); info.AddSignal(timeline, signal_value);