Merge branch 'shadps4-emu:main' into shadps4-splash-polish

This commit is contained in:
Arthur Cuesta 2026-06-08 13:10:57 -03:00 committed by GitHub
commit bd2c2126e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 3596 additions and 224 deletions

View File

@ -5,6 +5,7 @@ name: Build and Release
on:
push:
branches-ignore: [Pre-release-shadPS4-*]
paths-ignore:
- "documents/**"
- "**/*.md"

View File

@ -3,6 +3,7 @@
#include "common/arch.h"
#include "common/assert.h"
#include "emulator.h"
#if defined(ARCH_X86_64)
#define Crash() __asm__ __volatile__("int $3")
@ -13,13 +14,12 @@
#endif
void assert_fail_impl() {
Common::Log::Flush();
Common::Singleton<Core::Emulator>::Instance()->Shutdown();
Crash();
}
[[noreturn]] void unreachable_impl() {
Common::Log::Flush();
Crash();
assert_fail_impl();
throw std::runtime_error("Unreachable code");
}

View File

@ -1323,6 +1323,7 @@ s32 PS4_SYSV_ABI posix_select(s32 nfds, fd_set_posix* readfds, fd_set_posix* wri
}
if (native_fd == -1) {
LOG_WARNING(Kernel_Fs, "Unsupported fd {}", i);
continue;
}
@ -1454,6 +1455,7 @@ s32 PS4_SYSV_ABI posix_select(s32 nfds, fd_set* readfds, fd_set* writefds, fd_se
}
}();
if (native_fd == -1) {
LOG_WARNING(Kernel_Fs, "Unsupported fd {}", i);
continue;
}
host_to_guest.emplace(native_fd, i);

View File

@ -548,7 +548,7 @@ int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) {
return ret;
}
s32 PS4_SYSV_ABI sceKernelDebugRaiseException(s32 error, s64 unk) {
s32 PS4_SYSV_ABI sceKernelDebugRaiseException(u32 error, s64 unk) {
if (unk != 0) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
@ -556,7 +556,7 @@ s32 PS4_SYSV_ABI sceKernelDebugRaiseException(s32 error, s64 unk) {
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceKernelDebugRaiseExceptionOnReleaseMode(s32 error, s64 unk) {
s32 PS4_SYSV_ABI sceKernelDebugRaiseExceptionOnReleaseMode(u32 error, s64 unk) {
if (unk != 0) {
return ORBIS_KERNEL_ERROR_EINVAL;
}

File diff suppressed because it is too large Load Diff

View File

@ -54,6 +54,12 @@ enum OrbisHttpMethod : s32 {
ORBIS_HTTP_METHOD_CUSTOM = 8,
};
// mode argument for sceHttpAddRequestHeader.
enum OrbisHttpHeaderModes : s32 {
ORBIS_HTTP_HEADER_OVERWRITE = 0,
ORBIS_HTTP_HEADER_ADD = 1,
};
enum OrbisUriBuild : s32 {
ORBIS_HTTP_URI_BUILD_WITH_SCHEME = 0x01,
ORBIS_HTTP_URI_BUILD_WITH_HOSTNAME = 0x02,
@ -141,7 +147,6 @@ using OrbisHttpsCaList = Libraries::Ssl::OrbisSslCaList;
int PS4_SYSV_ABI sceHttpAddCookie(int libhttpCtxId, const char* url, const char* cookie,
u64 cookieLength);
int PS4_SYSV_ABI sceHttpAddQuery();
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(int libhttpCtxId);
@ -167,7 +172,6 @@ int PS4_SYSV_ABI sceHttpDbgShowConnectionStat();
int PS4_SYSV_ABI sceHttpDbgShowMemoryPoolStat();
int PS4_SYSV_ABI sceHttpDbgShowRequestStat();
int PS4_SYSV_ABI sceHttpDbgShowStat();
int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled(int id, int* isEnable);
int PS4_SYSV_ABI sceHttpGetAuthEnabled(int id, int* isEnable);
int PS4_SYSV_ABI sceHttpGetConnectionStat();
int PS4_SYSV_ABI sceHttpGetCookie(int libhttpCtxId, const char* url, char* cookie, u64* required,
@ -179,10 +183,8 @@ int PS4_SYSV_ABI sceHttpGetMemoryPoolStats(int libhttpCtxId, OrbisHttpMemoryPool
int PS4_SYSV_ABI sceHttpGetRegisteredCtxIds();
int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, u64 poolSize);
int PS4_SYSV_ABI sceHttpRedirectCacheFlush(int libhttpCtxId);
int PS4_SYSV_ABI sceHttpRemoveRequestHeader(int id, const char* name);
int PS4_SYSV_ABI sceHttpRequestGetAllHeaders();
int PS4_SYSV_ABI sceHttpSendRequest(int reqId, const void* postData, u64 size);
int PS4_SYSV_ABI sceHttpSetAcceptEncodingGZIPEnabled(int id, int isEnable);
int PS4_SYSV_ABI sceHttpSetAuthEnabled(int id, int isEnable);
int PS4_SYSV_ABI sceHttpSetAuthInfoCallback(int id, OrbisHttpAuthInfoCallback cbfunc,
void* userArg);
@ -203,15 +205,10 @@ int PS4_SYSV_ABI sceHttpSetEpollId();
int PS4_SYSV_ABI sceHttpSetHttp09Enabled(int id, int isEnable);
int PS4_SYSV_ABI sceHttpSetPolicyOption();
int PS4_SYSV_ABI sceHttpSetPriorityOption();
int PS4_SYSV_ABI sceHttpSetProxy();
int PS4_SYSV_ABI sceHttpSetRecvBlockSize(int id, u32 blockSize);
int PS4_SYSV_ABI sceHttpSetRedirectCallback(int id, OrbisHttpRedirectCallback cbfunc,
void* userArg);
int PS4_SYSV_ABI sceHttpSetRequestStatusCallback(int id, OrbisHttpRequestStatusCallback cbfunc,
void* userArg);
int PS4_SYSV_ABI sceHttpSetResolveRetry(int id, int retry);
int PS4_SYSV_ABI sceHttpSetResolveTimeOut(int id, u32 usec);
int PS4_SYSV_ABI sceHttpSetResponseHeaderMaxSize(int id, u64 headerSize);
int PS4_SYSV_ABI sceHttpSetSocketCreationCallback();
int PS4_SYSV_ABI sceHttpsFreeCaList(int libhttpCtxId, OrbisHttpsCaList* caList);
int PS4_SYSV_ABI sceHttpsGetCaList(int httpCtxId, OrbisHttpsCaList* list);
@ -222,11 +219,23 @@ int PS4_SYSV_ABI sceHttpsSetMinSslVersion(int id, int version);
int PS4_SYSV_ABI sceHttpsSetSslCallback(int id, OrbisHttpsCallback cbfunc, void* userArg);
int PS4_SYSV_ABI sceHttpsSetSslVersion(int id, int version);
int PS4_SYSV_ABI sceHttpsUnloadCert(int libhttpCtxId);
int PS4_SYSV_ABI sceHttpTerm(int libhttpCtxId);
int PS4_SYSV_ABI sceHttpWaitRequest(OrbisHttpEpollHandle eh, OrbisHttpNBEvent* nbev, int maxevents,
int timeout);
int PS4_SYSV_ABI sceHttpUriCopy();
//***********************************
// Init/Terminate functions
//***********************************
int PS4_SYSV_ABI sceHttpTerm(int libhttpCtxId);
//***********************************
// Misc functions
//***********************************
int PS4_SYSV_ABI sceHttpSetRecvBlockSize(int id, u32 blockSize);
int PS4_SYSV_ABI sceHttpSetProxy(int id, int httpProxyConf, int wlanProxyConf, const char* host,
u16 port);
int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled(int id, int* isEnable);
int PS4_SYSV_ABI sceHttpSetDefaultAcceptEncodingGZIPEnabled(int libhttpCtxId, int isEnable);
int PS4_SYSV_ABI sceHttpSetAcceptEncodingGZIPEnabled(int id, int isEnable);
//***********************************
// Non-blocking processing functions
//***********************************
int PS4_SYSV_ABI sceHttpCreateEpoll(int libhttpCtxId, OrbisHttpEpollHandle* eh);
@ -254,6 +263,8 @@ int PS4_SYSV_ABI sceHttpsEnableOptionPrivate(int id, u32 sslFlags);
//***********************************
// Response Information functions
//***********************************
int PS4_SYSV_ABI sceHttpSetResolveTimeOut(int id, u32 usec);
int PS4_SYSV_ABI sceHttpSetResponseHeaderMaxSize(int id, u64 headerSize);
int PS4_SYSV_ABI sceHttpGetAllResponseHeaders(int reqId, char** header, u64* headerSize);
int PS4_SYSV_ABI sceHttpGetResponseContentLength(int reqId, int* result, u64* contentLength);
int PS4_SYSV_ABI sceHttpGetStatusCode(int reqId, int* statusCode);
@ -261,6 +272,8 @@ int PS4_SYSV_ABI sceHttpSetInflateGZIPEnabled(int id, int isEnable);
//***********************************
// Http Header setting functions
//***********************************
int PS4_SYSV_ABI sceHttpAddRequestHeader(int id, const char* name, const char* value, s32 mode);
int PS4_SYSV_ABI sceHttpRemoveRequestHeader(int id, const char* name);
int PS4_SYSV_ABI sceHttpSetRequestContentLength(int id, u64 contentLength);
//***********************************
// Redirection setting functions
@ -270,6 +283,7 @@ int PS4_SYSV_ABI sceHttpSetAutoRedirect(int id, int isEnable);
//***********************************
// Timeout settting functions
//***********************************
int PS4_SYSV_ABI sceHttpSetResolveRetry(int id, int retry);
int PS4_SYSV_ABI sceHttpSetConnectTimeOut(int id, u32 usec);
int PS4_SYSV_ABI sceHttpSetSendTimeOut(int id, u32 usec);
int PS4_SYSV_ABI sceHttpSetRecvTimeOut(int id, u32 usec);

View File

@ -5,7 +5,7 @@
#include "core/libraries/error_codes.h"
// Base NP errors (0x80550001 0x8055001F)
// Base NP errors (0x80550001 - 0x8055001F)
constexpr int ORBIS_NP_ERROR_ALREADY_INITIALIZED = 0x80550001;
constexpr int ORBIS_NP_ERROR_NOT_INITIALIZED = 0x80550002;
constexpr int ORBIS_NP_ERROR_INVALID_ARGUMENT = 0x80550003;
@ -38,7 +38,7 @@ constexpr int ORBIS_NP_ERROR_CALLBACK_MAX = 0x8055001D;
constexpr int ORBIS_NP_ERROR_INVALID_NP_TITLE_ID = 0x8055001E;
constexpr int ORBIS_NP_ERROR_ONLINE_ID_CHANGED = 0x8055001F;
// NP Util (0x80550601 0x8055060e)
// NP Util (0x80550601 - 0x8055060e)
constexpr int ORBIS_NP_UTIL_ERROR_INVALID_ARGUMENT = 0x80550601;
constexpr int ORBIS_NP_UTIL_ERROR_INSUFFICIENT = 0x80550602;
constexpr int ORBIS_NP_UTIL_ERROR_PARSER_FAILED = 0x80550603;
@ -50,7 +50,7 @@ constexpr int ORBIS_NP_UTIL_ERROR_NOT_MATCH = 0x80550609;
constexpr int ORBIS_NP_UTIL_ERROR_INVALID_TITLEID = 0x8055060A;
constexpr int ORBIS_NP_UTIL_ERROR_UNKNOWN = 0x8055060E;
// NP Auth errors (0x80550300 0x8055040F)
// NP Auth errors (0x80550300 - 0x8055040F)
constexpr int ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT = 0x80550301;
constexpr int ORBIS_NP_AUTH_ERROR_INVALID_SIZE = 0x80550302;
constexpr int ORBIS_NP_AUTH_ERROR_OUT_OF_MEMORY = 0x80550303;
@ -94,7 +94,7 @@ constexpr int ORBIS_NP_AUTH_ERROR_ACCOUNT_RENEW_ACCOUNT16 = 0x8055044F;
constexpr int ORBIS_NP_AUTH_ERROR_SUB_ACCOUNT_RENEW_EULA = 0x8055044F;
constexpr int ORBIS_NP_AUTH_ERROR_UNKNOWN = 0x80550480;
// NP Community / Score client errors (0x80550700 0x8055071D)
// NP Community / Score client errors (0x80550700 - 0x8055071D)
constexpr int ORBIS_NP_COMMUNITY_ERROR_ALREADY_INITIALIZED = 0x80550701;
constexpr int ORBIS_NP_COMMUNITY_ERROR_NOT_INITIALIZED = 0x80550702;
constexpr int ORBIS_NP_COMMUNITY_ERROR_OUT_OF_MEMORY = 0x80550703;
@ -125,7 +125,7 @@ constexpr int ORBIS_NP_COMMUNITY_ERROR_GHOST_SERVER_RETURN_INVALID_STATUS_CODE =
constexpr int ORBIS_NP_COMMUNITY_ERROR_UBS_ONLINE_ID_IN_XML_CREATED_PAST_IS_DIFFERENT_FROM_CURRENT =
0x8055071D;
// NP Community / Score server errors (0x80550800 0x805508AB)
// NP Community / Score server errors (0x80550800 - 0x805508AB)
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_BAD_REQUEST = 0x80550801;
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_INVALID_TICKET = 0x80550802;
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_INVALID_SIGNATURE = 0x80550803;
@ -205,7 +205,7 @@ constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_UBS_MAINTENANCE = 0x805508B2;
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_BASIC_BLACKLISTED_USER_ID = 0x805508B3;
constexpr int ORBIS_NP_COMMUNITY_SERVER_ERROR_UNSPECIFIED = 0x805508FF;
// NP Matching2 (0x80550c01 0x80550d33)
// NP Matching2 (0x80550c01 - 0x80550d33)
constexpr int ORBIS_NP_MATCHING2_ERROR_OUT_OF_MEMORY = 0x80550C01;
constexpr int ORBIS_NP_MATCHING2_ERROR_ALREADY_INITIALIZED = 0x80550C02;
constexpr int ORBIS_NP_MATCHING2_ERROR_NOT_INITIALIZED = 0x80550C03;
@ -307,7 +307,7 @@ constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_NAT_TYPE_MISMATCH = 0x80550D31;
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_ROOM_INCONSISTENCY = 0x80550D32;
constexpr int ORBIS_NP_MATCHING2_SERVER_ERROR_BLOCKED_USER_IN_ROOM = 0x80550D33;
// NP Matching2 Signaling (0x80550e01 0x80550e1a)
// NP Matching2 Signaling (0x80550e01 - 0x80550e1a)
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_NOT_INITIALIZED = 0x80550E01;
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_ALREADY_INITIALIZED = 0x80550E02;
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_OUT_OF_MEMORY = 0x80550E03;
@ -335,7 +335,7 @@ constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_TERMINATED_BY_MYSELF = 0x80550E
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_MATCHING2_PEER_NOT_FOUND = 0x80550E19;
constexpr int ORBIS_NP_MATCHING2_SIGNALING_ERROR_OWN_PEER_ADDRESS = 0x80550E1A;
// NP Trophy (0x80551600 0x805516c2)
// NP Trophy (0x80551600 - 0x805516c2)
constexpr int ORBIS_NP_TROPHY_ERROR_UNKNOWN = 0x80551600;
constexpr int ORBIS_NP_TROPHY_ERROR_NOT_INITIALIZED = 0x80551601;
constexpr int ORBIS_NP_TROPHY_ERROR_ALREADY_INITIALIZED = 0x80551602;
@ -385,7 +385,7 @@ constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISPLAY_BUFFER_TOO_BIG = 0x805516
constexpr int ORBIS_NP_TROPHY_ERROR_SCREENSHOT_DISPLAY_BUFFER_RETRY_COUNT_MAX = 0x80551630;
constexpr int ORBIS_NP_TROPHY_ERROR_TITLE_NOT_FOUND = 0x805516C2;
// NP Bandwidth Test (0x80551f02 0x80551f09)
// NP Bandwidth Test (0x80551f02 - 0x80551f09)
constexpr int ORBIS_NP_BANDWIDTH_TEST_ERROR_NOT_INITIALIZED = 0x80551F02;
constexpr int ORBIS_NP_BANDWIDTH_TEST_ERROR_BAD_RESPONSE = 0x80551F03;
constexpr int ORBIS_NP_BANDWIDTH_TEST_ERROR_OUT_OF_MEMORY = 0x80551F04;
@ -395,7 +395,7 @@ constexpr int ORBIS_NP_BANDWIDTH_TEST_ERROR_CONTEXT_NOT_AVAILABLE = 0x80551F07;
constexpr int ORBIS_NP_BANDWIDTH_TEST_ERROR_ABORTED = 0x80551F08;
constexpr int ORBIS_NP_BANDWIDTH_TEST_ERROR_TIMEOUT = 0x80551F09;
// NP Party (0x80552501 0x80552516)
// NP Party (0x80552501 - 0x80552516)
constexpr int ORBIS_NP_PARTY_ERROR_UNKNOWN = 0x80552501;
constexpr int ORBIS_NP_PARTY_ERROR_ALREADY_INITIALIZED = 0x80552502;
constexpr int ORBIS_NP_PARTY_ERROR_NOT_INITIALIZED = 0x80552503;
@ -413,7 +413,7 @@ constexpr int ORBIS_NP_PARTY_ERROR_GAME_SESSION_NOT_ENABLED = 0x80552514;
constexpr int ORBIS_NP_PARTY_ERROR_INVALID_PARTY_NO_FRIENDS = 0x80552515;
constexpr int ORBIS_NP_PARTY_ERROR_INVALID_PARTY_IS_GAME_SESSION = 0x80552516;
// NP Signaling (0x80552701 0x8055271b)
// NP Signaling (0x80552701 - 0x8055271b)
constexpr int ORBIS_NP_SIGNALING_ERROR_NOT_INITIALIZED = 0x80552701;
constexpr int ORBIS_NP_SIGNALING_ERROR_ALREADY_INITIALIZED = 0x80552702;
constexpr int ORBIS_NP_SIGNALING_ERROR_OUT_OF_MEMORY = 0x80552703;
@ -442,7 +442,7 @@ constexpr int ORBIS_NP_SIGNALING_ERROR_PROHIBITED_TO_USE = 0x80552719;
constexpr int ORBIS_NP_SIGNALING_ERROR_EXCEED_RATE_LIMIT = 0x8055271A;
constexpr int ORBIS_NP_SIGNALING_ERROR_OWN_PEER_ADDRESS = 0x8055271B;
// NP WebAPI (0x80552901 0x8055291f)
// NP WebAPI (0x80552901 - 0x8055291f)
constexpr int ORBIS_NP_WEBAPI_ERROR_OUT_OF_MEMORY = 0x80552901;
constexpr int ORBIS_NP_WEBAPI_ERROR_INVALID_ARGUMENT = 0x80552902;
constexpr int ORBIS_NP_WEBAPI_ERROR_INVALID_LIB_CONTEXT_ID = 0x80552903;
@ -475,7 +475,7 @@ constexpr int ORBIS_NP_WEBAPI_ERROR_EXTD_PUSH_EVENT_CALLBACK_NOT_FOUND = 0x80552
constexpr int ORBIS_NP_WEBAPI_ERROR_AFTER_SEND = 0x8055291E;
constexpr int ORBIS_NP_WEBAPI_ERROR_TIMEOUT = 0x8055291F;
// NP In-Game Message (0x80552b01 0x80552b09)
// NP In-Game Message (0x80552b01 - 0x80552b09)
constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_OUT_OF_MEMORY = 0x80552B01;
constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_INVALID_ARGUMENT = 0x80552B02;
constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_LIB_CONTEXT_NOT_FOUND = 0x80552B03;
@ -486,13 +486,13 @@ constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_SIGNED_IN_USER_NOT_FOUND = 0x80552B
constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_NOT_PREPARED = 0x80552B08;
constexpr int ORBIS_NP_IN_GAME_MESSAGE_ERROR_EXCEED_RATE_LIMIT = 0x80552B09;
// NP ID Mapper (0x80553000 0x80553003)
// NP ID Mapper (0x80553000 - 0x80553003)
constexpr int ORBIS_NP_ID_MAPPER_ERROR_ABORTED = 0x80553000;
constexpr int ORBIS_NP_ID_MAPPER_ERROR_ACCOUNT_ID_NOT_FOUND = 0x80553001;
constexpr int ORBIS_NP_ID_MAPPER_ERROR_ONLINE_ID_NOT_FOUND = 0x80553002;
constexpr int ORBIS_NP_ID_MAPPER_ERROR_NP_ID_NOT_FOUND = 0x80553003;
// NP Data Communication (0x80553200 0x80553218)
// NP Data Communication (0x80553200 - 0x80553218)
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_UNKNOWN = 0x80553200;
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_NOT_INITIALIZED = 0x80553201;
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_ALREADY_INITIALIZED = 0x80553202;
@ -519,7 +519,7 @@ constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_SIGNALING_EXCEEDS_MAX = 0x805532
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_DATA_CHANNEL_EXCEEDS_MAX = 0x80553217;
constexpr int ORBIS_NP_DATA_COMMUNICATION_ERROR_INSUFFICIENT_BUFFER = 0x80553218;
// NP Session Signaling (0x80553301 0x80553312)
// NP Session Signaling (0x80553301 - 0x80553312)
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_NOT_INITIALIZED = 0x80553301;
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_ALREADY_INITIALIZED = 0x80553302;
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_INVALID_ARGUMENT = 0x80553303;
@ -539,7 +539,7 @@ constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_TERMINATED_BY_MYSELF = 0x80553310
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_TOO_MANY_CONN = 0x80553311;
constexpr int ORBIS_NP_SESSION_SIGNALING_ERROR_GROUP_SIGNALING_ALREADY_ACTIVATED = 0x80553312;
// NP WEBAPI2 (0x80553401 0x8055341c)
// NP WEBAPI2 (0x80553401 - 0x8055341c)
constexpr int ORBIS_NP_WEBAPI2_ERROR_OUT_OF_MEMORY = 0x80553401;
constexpr int ORBIS_NP_WEBAPI2_ERROR_INVALID_ARGUMENT = 0x80553402;
constexpr int ORBIS_NP_WEBAPI2_ERROR_INVALID_LIB_CONTEXT_ID = 0x80553403;
@ -569,7 +569,7 @@ constexpr int ORBIS_NP_WEBAPI2_AFTER_SEND = 0x8055341a;
constexpr int ORBIS_NP_WEBAPI2_TIMEOUT = 0x8055341b;
constexpr int ORBIS_NP_WEBAPI2_PUSH_CONTEXT_NOT_FOUND = 0x8055341c;
// NP Session Management Client (0x80553600 0x8055361e)
// NP Session Management Client (0x80553600 - 0x8055361e)
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_ALREADY_INITIALIZED = 0x80553600;
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_NOT_INITIALIZED = 0x80553601;
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_OUT_OF_MEMORY = 0x80553602;
@ -602,7 +602,7 @@ constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_VIEW_NAME = 0x805
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_EVENT = 0x8055361D;
constexpr int ORBIS_NP_SESSION_MANAGEMENT_CLIENT_ERROR_INVALID_MEMBER_ID = 0x8055361E;
// NP Session Management Manager (0x80553700 0x80553736)
// NP Session Management Manager (0x80553700 - 0x80553736)
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_ALREADY_INITIALIZED = 0x80553700;
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_NOT_INITIALIZED = 0x80553701;
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_OUT_OF_MEMORY = 0x80553702;
@ -661,7 +661,7 @@ constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_BRIDGE_INFO_NOT_FOUND =
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_ITEM_NOT_FOUND = 0x80553735;
constexpr int ORBIS_NP_SESSION_MANAGEMENT_MANAGER_ERROR_INVALID_SESSION_CUSTOM_DATA = 0x80553736;
// NP Game Intent (0x80553800 0x80553807)
// NP Game Intent (0x80553800 - 0x80553807)
constexpr int ORBIS_NP_GAME_INTENT_ERROR_UNKNOWN = 0x80553800;
constexpr int ORBIS_NP_GAME_INTENT_ERROR_ALREADY_INITIALIZED = 0x80553801;
constexpr int ORBIS_NP_GAME_INTENT_ERROR_NOT_INITIALIZED = 0x80553802;
@ -671,6 +671,6 @@ constexpr int ORBIS_NP_GAME_INTENT_ERROR_INSUFFICIENT_BUFFER = 0x80553805;
constexpr int ORBIS_NP_GAME_INTENT_ERROR_INTENT_NOT_FOUND = 0x80553806;
constexpr int ORBIS_NP_GAME_INTENT_ERROR_VALUE_NOT_FOUND = 0x80553807;
// NP ASM Client (0x8055a287 0x8055a289)
// NP ASM Client (0x8055a287 - 0x8055a289)
constexpr int ORBIS_NP_ASM_CLIENT_ERROR_ABORTED = 0x8055A287;
constexpr int ORBIS_NP_ASM_CLIENT_ERROR_NP_SERVICE_LAVEL_NOT_MATCH = 0x8055A289;

View File

@ -125,6 +125,8 @@ struct ContextKeyHash {
struct TrophyContext {
u32 context_id;
u32 service_label;
u32 user_id;
bool registered = false;
std::filesystem::path trophy_xml_path; // resolved once at CreateContext
std::filesystem::path xml_dir; // .../Xml/
@ -216,29 +218,11 @@ s32 PS4_SYSV_ABI sceNpTrophyCreateContext(OrbisNpTrophyContext* context,
auto& ctx = contexts_internal[key];
ctx.context_id = *context;
// Resolve and cache all paths once so callers never recompute them.
std::string np_comm_id;
const auto& trophyMap = Common::ElfInfo::Instance().GetTrophyIndexMap();
auto it = trophyMap.find(service_label);
if (it != trophyMap.end()) {
np_comm_id = it->second;
} else {
LOG_ERROR(Lib_NpTrophy, "No npCommId found for trophy index/service_label: {}",
service_label);
return ORBIS_NP_TROPHY_ERROR_UNKNOWN;
}
const auto trophy_base =
Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "trophy" / np_comm_id;
ctx.xml_save_file =
EmulatorSettings.GetHomeDir() / std::to_string(user_id) / "trophy" / (np_comm_id + ".xml");
ctx.xml_dir = trophy_base / "Xml";
ctx.icons_dir = trophy_base / "Icons";
ctx.trophy_xml_path = GetTrophyXmlPath(ctx.xml_dir, EmulatorSettings.GetConsoleLanguage());
ctx.service_label = service_label;
ctx.user_id = user_id;
LOG_INFO(Lib_NpTrophy, "New context = {}, user_id = {} service label = {}", *context, user_id,
service_label);
return ORBIS_OK;
}
@ -836,12 +820,34 @@ int PS4_SYSV_ABI sceNpTrophyRegisterContext(OrbisNpTrophyContext context,
ContextKey contextkey = trophy_contexts[contextId];
auto& ctx = contexts_internal[contextkey];
if (ctx.registered)
if (ctx.registered) {
return ORBIS_NP_TROPHY_ERROR_ALREADY_REGISTERED;
}
if (!std::filesystem::exists(ctx.trophy_xml_path)) {
LOG_ERROR(Lib_NpTrophy, "Could not find trophy files.");
// Stub success here to prevent issues specific to missing a trophy key.
// Resolve trophy-related paths using the context's service_label
std::string np_comm_id;
const auto& trophyMap = Common::ElfInfo::Instance().GetTrophyIndexMap();
auto it = trophyMap.find(ctx.service_label);
if (it != trophyMap.end()) {
// If we have an NP communication ID, prepare proper trophy paths
np_comm_id = it->second;
const auto trophy_base =
Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "trophy" / np_comm_id;
ctx.xml_save_file = EmulatorSettings.GetHomeDir() / std::to_string(ctx.user_id) / "trophy" /
(np_comm_id + ".xml");
ctx.xml_dir = trophy_base / "Xml";
ctx.icons_dir = trophy_base / "Icons";
ctx.trophy_xml_path = GetTrophyXmlPath(ctx.xml_dir, EmulatorSettings.GetConsoleLanguage());
if (!std::filesystem::exists(ctx.trophy_xml_path)) {
LOG_ERROR(Lib_NpTrophy, "Could not find trophy files.");
// Stub success here to prevent issues specific to missing a trophy key.
}
} else {
LOG_ERROR(Lib_NpTrophy, "No npCommId found for trophy index/service_label: {}",
ctx.service_label);
return ORBIS_NP_UTIL_ERROR_INVALID_TITLEID;
}
ctx.registered = true;

View File

@ -556,18 +556,12 @@ int PS4_SYSV_ABI scePadResetLightBar(s32 handle) {
s32 colour_index = u ? u->user_color - 1 : 0;
Input::Colour colour{255, 0, 0};
if (colour_index >= 0 && colour_index <= 3) {
static constexpr Input::Colour colours[4]{
{0, 0, 255}, // blue
{255, 0, 0}, // red
{0, 255, 0}, // green
{255, 0, 255}, // pink
};
colour = colours[colour_index];
colour = Input::g_user_colours[colour_index];
} else {
LOG_ERROR(Lib_Pad, "Invalid user colour value {} for controller {}, falling back to blue",
colour_index, handle);
}
controller.SetLightBarRGB(colour.r, colour.g, colour.b);
controller.SetLightBarRGB(colour);
return ORBIS_OK;
}

View File

@ -7,6 +7,7 @@
#include "common/signal_context.h"
#include "core/libraries/kernel/threads/exception.h"
#include "core/signals.h"
#include "emulator.h"
#ifdef _WIN32
#include <windows.h>
@ -52,10 +53,6 @@ static LONG WINAPI SignalHandler(EXCEPTION_POINTERS* pExp) noexcept {
case DBG_PRINTEXCEPTION_WIDE_C:
// Used by OutputDebugString functions.
return EXCEPTION_CONTINUE_EXECUTION;
case EXCEPTION_BREAKPOINT:
// This is almost certainly coming from our asserts/unreachables, no need to log it again.
Common::Log::Flush();
return EXCEPTION_CONTINUE_SEARCH;
default:
break;
}
@ -64,8 +61,11 @@ static LONG WINAPI SignalHandler(EXCEPTION_POINTERS* pExp) noexcept {
return EXCEPTION_CONTINUE_EXECUTION;
}
LOG_CRITICAL(Debug, "Unhandled Exception code {:#x} at {}", code, address);
Common::Log::Flush();
// Breakpoints almost certainly come from our asserts/unreachables, no need to log it again.
if (code != EXCEPTION_BREAKPOINT) {
LOG_CRITICAL(Debug, "Unhandled Exception code {:#x} at {}", code, address);
Common::Singleton<Core::Emulator>::Instance()->Shutdown();
}
return EXCEPTION_CONTINUE_SEARCH;
}

View File

@ -57,6 +57,8 @@ Frontend::WindowSDL* g_window = nullptr;
namespace Core {
std::mutex exit_mutex{};
Emulator::Emulator() {
// Initialize NT API functions, set high priority and disable WER
#ifdef _WIN32
@ -68,10 +70,26 @@ Emulator::Emulator() {
WSADATA wsaData;
WSAStartup(versionWanted, &wsaData);
#endif
std::at_quick_exit([]() { Common::Singleton<Core::Emulator>::Instance()->Shutdown(); });
}
Emulator::~Emulator() {}
void Emulator::Shutdown() {
static bool exit_done = false;
std::scoped_lock l{exit_mutex};
if (exit_done) {
return;
}
Common::Log::Flush();
if (controllers) {
controllers->ResetLightbarColors();
// need to give SDL time to do this before the runtime exits
std::this_thread::sleep_for(std::chrono::milliseconds{10});
}
exit_done = true;
}
s32 ReadCompiledSdkVersion(const std::filesystem::path& file) {
Core::Loader::Elf elf;
elf.Open(file);
@ -102,8 +120,13 @@ std::map<s32, std::string> ExtractTrophies(const std::filesystem::path& npbind_p
}
auto np_comm_ids = npbind.GetNpCommIds();
if (!std::filesystem::exists(trophy_dir) || np_comm_ids.empty()) {
LOG_WARNING(Common_Filesystem, "Cannot extract game trophies");
if (np_comm_ids.empty()) {
LOG_WARNING(Common_Filesystem, "No NPCommIDs in npbind.dat");
return trophy_index_map;
}
if (!std::filesystem::exists(trophy_dir)) {
LOG_WARNING(Common_Filesystem, "Game does not contain a trophy directory");
return trophy_index_map;
}
@ -128,8 +151,13 @@ std::map<s32, std::string> ExtractTrophies(const std::filesystem::path& npbind_p
continue;
}
// Extract the actual trophies if they're no extracted yet
// Add the relevant trophies to our trophy index map.
// This currently assumes the order of NPCommIDs matches the order of trophies.
std::string np_comm_id = np_comm_ids[trophy_index];
trophy_index_map[trophy_index] = np_comm_id;
LOG_DEBUG(Loader, "Mapped trophy index {} to NPCommID: {}", trophy_index, np_comm_id);
// Extract the actual trophies if they're no extracted yet
const auto& trophy_output_dir =
Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "trophy" / np_comm_id;
if (!std::filesystem::exists(trophy_output_dir)) {
@ -153,11 +181,6 @@ std::map<s32, std::string> ExtractTrophies(const std::filesystem::path& npbind_p
user_trophy_file, discard);
}
}
// Add the relevant trophies to our trophy index map.
// This currently assumes the order of NPCommIDs matches the order of trophies.
trophy_index_map[trophy_index] = np_comm_id;
LOG_DEBUG(Loader, "Mapped trophy index {} to NPCommID: {}", trophy_index, np_comm_id);
}
}
return trophy_index_map;

View File

@ -29,6 +29,7 @@ public:
void Run(std::filesystem::path file, std::vector<std::string> args = {},
std::optional<std::filesystem::path> game_folder = {});
void UpdatePlayTime(const std::string& serial);
void Shutdown();
/**
* This will kill the current process and launch a new process with the same configuration

View File

@ -150,7 +150,7 @@ void GameController::UpdateAxisSmoothing() {
m_state.UpdateAxisSmoothing();
}
void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) {
void GameController::SetLightBarRGB(u8 const r, u8 const g, u8 const b) {
if (override_colour.has_value()) {
return;
}
@ -160,6 +160,10 @@ void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) {
}
}
void GameController::SetLightBarRGB(Colour const c) {
SetLightBarRGB(c.r, c.g, c.b);
}
Colour GameController::GetLightBarRGB() {
return colour;
}
@ -170,6 +174,22 @@ void GameController::PollLightColour() {
}
}
void GameControllers::ResetLightbarColors() {
for (auto& c : controllers) {
auto const* u = UserManagement.GetUserByID(c->user_id);
if (!u || !c->m_sdl_gamepad) {
continue;
}
auto const i = u->user_color - 1;
if (i < 0 || i > 3) {
continue;
}
auto const& col = g_user_colours[i];
c->override_colour = std::nullopt;
c->SetLightBarRGB(col);
}
}
bool GameController::SetVibration(u8 smallMotor, u8 largeMotor) {
if (m_sdl_gamepad != nullptr) {
return SDL_RumbleGamepad(m_sdl_gamepad, (smallMotor / 255.0f) * 0xFFFF,

View File

@ -43,6 +43,12 @@ struct TouchpadEntry {
struct Colour {
u8 r, g, b;
};
static constexpr Input::Colour g_user_colours[4]{
{0, 0, 255}, // blue
{255, 0, 0}, // red
{0, 255, 0}, // green
{255, 0, 255}, // pink
};
struct State {
private:
@ -127,7 +133,8 @@ public:
void UpdateGyro(const float gyro[3]);
void UpdateAcceleration(const float acceleration[3]);
void UpdateAxisSmoothing();
void SetLightBarRGB(u8 r, u8 g, u8 b);
void SetLightBarRGB(u8 const r, u8 const g, u8 const b);
void SetLightBarRGB(Colour const c);
Colour GetLightBarRGB();
void PollLightColour();
bool SetVibration(u8 smallMotor, u8 largeMotor);
@ -205,6 +212,7 @@ public:
controllers[i]->SetLightBarRGB(r, g, b);
controllers[i]->override_colour = {r, g, b};
}
void ResetLightbarColors();
};
} // namespace Input

View File

@ -236,6 +236,12 @@ spv::ExecutionMode ExecutionMode(AmdGpu::TessellationPartitioning spacing) {
return spv::ExecutionMode::SpacingFractionalOdd;
case AmdGpu::TessellationPartitioning::FracEven:
return spv::ExecutionMode::SpacingFractionalEven;
case AmdGpu::TessellationPartitioning::Pow2:
// Pow2 rounds tessellation factors to the nearest power of 2, which has no
// direct Vulkan equivalent. SpacingEqual (integer) is the closest match.
LOG_WARNING(Render_Vulkan, "Tessellation partitioning Pow2 has no Vulkan equivalent, "
"falling back to SpacingEqual");
return spv::ExecutionMode::SpacingEqual;
default:
break;
}

View File

@ -139,6 +139,10 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, u32 index) {
case IR::Attribute::BaryCoordNoPersp:
return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.bary_coord_nopersp,
ctx.ConstU32(comp)));
case IR::Attribute::BaryCoordNoPerspSample:
return ctx.OpLoad(
ctx.F32[1],
ctx.OpAccessChain(ctx.input_f32, ctx.bary_coord_nopersp_sample, ctx.ConstU32(comp)));
default:
UNREACHABLE_MSG("Read attribute {}", attr);
}

View File

@ -390,6 +390,16 @@ void EmitContext::DefineInputs() {
spv::StorageClass::Input);
}
}
if (info.loads.GetAny(IR::Attribute::BaryCoordNoPerspSample)) {
if (profile.supports_amd_shader_explicit_vertex_parameter) {
bary_coord_nopersp_sample = DefineVariable(
F32[2], spv::BuiltIn::BaryCoordNoPerspSampleAMD, spv::StorageClass::Input);
} else if (profile.supports_fragment_shader_barycentric) {
bary_coord_nopersp_sample = DefineVariable(
F32[3], spv::BuiltIn::BaryCoordNoPerspKHR, spv::StorageClass::Input);
// Decorate(bary_coord_nopersp_sample, spv::Decoration::Sample);
}
}
const bool has_clip_distance_inputs = runtime_info.fs_info.clip_distance_emulation;
// Clip distances attribute vector is the last in inputs array

View File

@ -288,6 +288,7 @@ public:
Id bary_coord_smooth_centroid{};
Id bary_coord_smooth_sample{};
Id bary_coord_nopersp{};
Id bary_coord_nopersp_sample{};
struct TextureDefinition {
const VectorIds* data_types;

View File

@ -221,7 +221,16 @@ constexpr NumberFormat RemapNumberFormat(const NumberFormat format, const DataFo
}
}
case NumberFormat::Srgb:
return data_format == DataFormat::FormatBc6 ? NumberFormat::Unorm : format;
switch (data_format) {
case DataFormat::FormatBc4:
case DataFormat::FormatBc5:
case DataFormat::FormatBc6:
// BC4/BC5 store non-color data (single/two-channel, used for normal maps),
// and BC6 is HDR float — none have sRGB Vulkan equivalents.
return NumberFormat::Unorm;
default:
return format;
}
case NumberFormat::Uscaled:
return NumberFormat::Uint;
case NumberFormat::Sscaled:
@ -299,8 +308,16 @@ constexpr NumberConversion MapNumberConversion(const NumberFormat num_fmt,
}
}
case NumberFormat::Srgb:
return data_fmt == DataFormat::FormatBc6 ? NumberConversion::SrgbToNorm
: NumberConversion::None;
switch (data_fmt) {
case DataFormat::FormatBc4:
case DataFormat::FormatBc5:
// BC4/BC5 have no sRGB variant; no conversion needed (treated as Unorm).
return NumberConversion::None;
case DataFormat::FormatBc6:
return NumberConversion::SrgbToNorm;
default:
return NumberConversion::None;
}
case NumberFormat::Uscaled:
return NumberConversion::UintToUscaled;
case NumberFormat::Sscaled:

View File

@ -35,6 +35,20 @@ vk::StencilOp StencilOp(AmdGpu::StencilFunc op) {
return vk::StencilOp::eDecrementAndWrap;
case AmdGpu::StencilFunc::ReplaceOp:
return vk::StencilOp::eReplace;
case AmdGpu::StencilFunc::Ones:
LOG_WARNING(Render_Vulkan, "Unsupported stencil op {}, using Replace.",
static_cast<u32>(op));
return vk::StencilOp::eReplace;
case AmdGpu::StencilFunc::And:
case AmdGpu::StencilFunc::Or:
case AmdGpu::StencilFunc::Xor:
case AmdGpu::StencilFunc::Nand:
case AmdGpu::StencilFunc::Nor:
case AmdGpu::StencilFunc::Xnor:
// Bitwise stencil operations have no Vulkan equivalent; eKeep is the safest fallback.
LOG_WARNING(Render_Vulkan, "Unsupported bitwise stencil op {}, using Keep.",
static_cast<u32>(op));
return vk::StencilOp::eKeep;
default:
UNREACHABLE();
return vk::StencilOp::eKeep;

View File

@ -25,6 +25,7 @@ set(SETTINGS_TEST_SOURCES
# Stubs that replace dependencies
stubs/common_stub.cpp
stubs/core_stub.cpp
stubs/scm_rev_stub.cpp
stubs/sdl_stub.cpp
@ -115,6 +116,8 @@ set(GCN_TEST_SOURCES
stubs/common_stub.cpp
stubs/resource_tracking_pass_stub.cpp
stubs/scm_rev_stub.cpp
stubs/sdl_stub.cpp
stubs/core_stub.cpp
gcn/gcn_test_runner.hpp
gcn/gcn_test_runner.cpp
@ -209,7 +212,6 @@ endforeach()
set(HTTP_TEST_SOURCES
# Under test
${CMAKE_SOURCE_DIR}/src/core/libraries/network/http.cpp
# Required to link RegisterLib's LIB_FUNCTION calls and the logger's
# access to EmulatorSettings.
${CMAKE_SOURCE_DIR}/src/core/emulator_settings.cpp
@ -227,11 +229,14 @@ set(HTTP_TEST_SOURCES
stubs/scm_rev_stub.cpp
stubs/sdl_stub.cpp
stubs/loader_stub.cpp
stubs/core_stub.cpp
stubs/kernel_stub.cpp
# Tests
network/test_http_uri.cpp
network/test_http_status_line.cpp
network/test_http_parse_response_header.cpp
network/test_http_lifecycle.cpp
)
add_executable(shadps4_http_test ${HTTP_TEST_SOURCES})

File diff suppressed because it is too large Load Diff

12
tests/stubs/core_stub.cpp Normal file
View File

@ -0,0 +1,12 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "emulator.h"
namespace Core {
Emulator::Emulator() {}
Emulator::~Emulator() {}
void Emulator::Shutdown() {}
} // namespace Core

View File

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "tests/stubs/kernel_stub.h"
#include "core/libraries/kernel/process.h"
namespace Libraries::Kernel {
static constexpr s32 DefaultTestSdkVersion = 0x4500000;
static s32 g_test_sdk_version = DefaultTestSdkVersion;
void TestSetSdkVersion(s32 ver) {
g_test_sdk_version = ver;
}
void TestResetSdkVersion() {
g_test_sdk_version = DefaultTestSdkVersion;
}
s32 PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(s32* ver) {
if (ver) {
*ver = g_test_sdk_version;
}
return 0;
}
} // namespace Libraries::Kernel

13
tests/stubs/kernel_stub.h Normal file
View File

@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Libraries::Kernel {
void TestSetSdkVersion(s32 ver);
void TestResetSdkVersion();
} // namespace Libraries::Kernel

View File

@ -2,17 +2,23 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <SDL3/SDL_messagebox.h>
#include "sdl_window.h"
namespace Frontend {
WindowSDL::~WindowSDL() = default;
}
extern "C" {
bool SDL_ShowMessageBox(const SDL_MessageBoxData* /* messageboxdata */, int* buttonid) {
if (buttonid) *buttonid = 0; // "No",skip migration
return true;
}
bool SDL_ShowMessageBox(const SDL_MessageBoxData* /* messageboxdata */, int* buttonid) {
if (buttonid)
*buttonid = 0; // "No",skip migration
return true;
}
bool SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags /* flags */, const char* /* title */,
const char* /* message */, SDL_Window* /* window */) {
return true;
}
bool SDL_ShowSimpleMessageBox(SDL_MessageBoxFlags /* flags */, const char* /* title */,
const char* /* message */, SDL_Window* /* window */) {
return true;
}
} // extern "C"