Merge branch 'master' into wiimote-to-guncon3

This commit is contained in:
Ani 2026-03-15 18:56:40 +01:00 committed by GitHub
commit bf29b23ed7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
251 changed files with 8363 additions and 1943 deletions

View File

@ -17,7 +17,7 @@ brew install -f --overwrite --quiet ccache "llvm@$LLVM_COMPILER_VER"
brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER"
if [ "$AARCH64" -eq 1 ]; then
brew install -f --overwrite --quiet googletest opencv@4 sdl3 vulkan-headers vulkan-loader molten-vk
brew unlink --quiet ffmpeg fmt qtbase qtsvg qtdeclarative protobuf
brew unlink --quiet ffmpeg fmt qtbase qtsvg qtdeclarative
else
arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
arch -x86_64 /usr/local/bin/brew install -f --overwrite --quiet python@3.14 opencv@4 "llvm@$LLVM_COMPILER_VER" sdl3 vulkan-headers vulkan-loader molten-vk
@ -122,6 +122,7 @@ cmake .. \
-DUSE_SYSTEM_MVK=ON \
-DUSE_SYSTEM_SDL=ON \
-DUSE_SYSTEM_OPENCV=ON \
-DUSE_SYSTEM_PROTOBUF=ON \
-G Ninja
fi

View File

@ -30,23 +30,23 @@ jobs:
matrix:
include:
- os: ubuntu-24.04
docker_img: "rpcs3/rpcs3-ci-jammy:1.8"
docker_img: "rpcs3/rpcs3-ci-jammy:1.9"
build_sh: "/rpcs3/.ci/build-linux.sh"
compiler: clang
UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux"
- os: ubuntu-24.04
docker_img: "rpcs3/rpcs3-ci-jammy:1.8"
docker_img: "rpcs3/rpcs3-ci-jammy:1.9"
build_sh: "/rpcs3/.ci/build-linux.sh"
compiler: gcc
- os: ubuntu-24.04-arm
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.8"
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.9"
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
compiler: clang
UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64"
- os: ubuntu-24.04-arm
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.8"
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.9"
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
compiler: gcc
name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }}

2
3rdparty/FAudio vendored

@ -1 +1 @@
Subproject commit e67d761ead486de3e69fa11705456bf94df734ca
Subproject commit dc034fc671b07bbd14e8410d5dd6be6da38fdf6d

@ -1 +1 @@
Subproject commit 75c00596307bf05ba7bbc8c7022836bf52f17477
Subproject commit c41d64c6a35f6174bf4a27010aeac52a8d3bb2c6

2
3rdparty/curl/curl vendored

@ -1 +1 @@
Subproject commit 400fffa90f30c7a2dc762fa33009d24851bd2016
Subproject commit 8c908d2d0a6d32abdedda2c52e90bd56ec76c24d

View File

@ -79,12 +79,16 @@
<ClCompile Include="curl\lib\cookie.c" />
<ClCompile Include="curl\lib\cshutdn.c" />
<ClCompile Include="curl\lib\curlx\base64.c" />
<ClCompile Include="curl\lib\curlx\basename.c" />
<ClCompile Include="curl\lib\curlx\dynbuf.c" />
<ClCompile Include="curl\lib\curlx\fopen.c" />
<ClCompile Include="curl\lib\curlx\inet_ntop.c" />
<ClCompile Include="curl\lib\curlx\inet_pton.c" />
<ClCompile Include="curl\lib\curlx\multibyte.c" />
<ClCompile Include="curl\lib\curlx\nonblock.c" />
<ClCompile Include="curl\lib\curlx\snprintf.c" />
<ClCompile Include="curl\lib\curlx\strcopy.c" />
<ClCompile Include="curl\lib\curlx\strdup.c" />
<ClCompile Include="curl\lib\curlx\strerr.c" />
<ClCompile Include="curl\lib\curlx\strparse.c" />
<ClCompile Include="curl\lib\curlx\timediff.c" />
@ -106,6 +110,7 @@
<ClCompile Include="curl\lib\curl_rtmp.c" />
<ClCompile Include="curl\lib\curl_sasl.c" />
<ClCompile Include="curl\lib\curl_sha512_256.c" />
<ClCompile Include="curl\lib\curl_share.c" />
<ClCompile Include="curl\lib\curl_sspi.c" />
<ClCompile Include="curl\lib\curl_threads.c" />
<ClCompile Include="curl\lib\curl_trc.c" />
@ -169,14 +174,13 @@
<ClCompile Include="curl\lib\progress.c" />
<ClCompile Include="curl\lib\psl.c" />
<ClCompile Include="curl\lib\rand.c" />
<ClCompile Include="curl\lib\rename.c" />
<ClCompile Include="curl\lib\ratelimit.c" />
<ClCompile Include="curl\lib\request.c" />
<ClCompile Include="curl\lib\rtsp.c" />
<ClCompile Include="curl\lib\select.c" />
<ClCompile Include="curl\lib\sendf.c" />
<ClCompile Include="curl\lib\setopt.c" />
<ClCompile Include="curl\lib\sha256.c" />
<ClCompile Include="curl\lib\share.c" />
<ClCompile Include="curl\lib\slist.c" />
<ClCompile Include="curl\lib\smb.c" />
<ClCompile Include="curl\lib\smtp.c" />
@ -184,10 +188,8 @@
<ClCompile Include="curl\lib\socks.c" />
<ClCompile Include="curl\lib\socks_gssapi.c" />
<ClCompile Include="curl\lib\socks_sspi.c" />
<ClCompile Include="curl\lib\speedcheck.c" />
<ClCompile Include="curl\lib\splay.c" />
<ClCompile Include="curl\lib\strcase.c" />
<ClCompile Include="curl\lib\strdup.c" />
<ClCompile Include="curl\lib\strequal.c" />
<ClCompile Include="curl\lib\strerror.c" />
<ClCompile Include="curl\lib\system_win32.c" />
@ -204,6 +206,7 @@
<ClCompile Include="curl\lib\version.c" />
<ClCompile Include="curl\lib\vquic\curl_ngtcp2.c" />
<ClCompile Include="curl\lib\vquic\curl_quiche.c" />
<ClCompile Include="curl\lib\vssh\vssh.c" />
<ClCompile Include="curl\lib\vtls\apple.c" />
<ClCompile Include="curl\lib\vtls\cipher_suite.c" />
<ClCompile Include="curl\lib\vtls\hostcheck.c" />
@ -224,13 +227,11 @@
<ClCompile Include="curl\lib\vauth\spnego_sspi.c" />
<ClCompile Include="curl\lib\vauth\vauth.c" />
<ClCompile Include="curl\lib\vquic\vquic.c" />
<ClCompile Include="curl\lib\vssh\curl_path.c" />
<ClCompile Include="curl\lib\vssh\libssh.c" />
<ClCompile Include="curl\lib\vssh\libssh2.c" />
<ClCompile Include="curl\lib\vtls\gtls.c" />
<ClCompile Include="curl\lib\vtls\keylog.c" />
<ClCompile Include="curl\lib\vtls\mbedtls.c" />
<ClCompile Include="curl\lib\vtls\mbedtls_threadlock.c" />
<ClCompile Include="curl\lib\vtls\openssl.c" />
<ClCompile Include="curl\lib\vtls\schannel.c" />
<ClCompile Include="curl\lib\vtls\schannel_verify.c" />
@ -272,6 +273,7 @@
<ClInclude Include="curl\lib\cookie.h" />
<ClInclude Include="curl\lib\cshutdn.h" />
<ClInclude Include="curl\lib\curlx\base64.h" />
<ClInclude Include="curl\lib\curlx\basename.h" />
<ClInclude Include="curl\lib\curlx\binmode.h" />
<ClInclude Include="curl\lib\curlx\curlx.h" />
<ClInclude Include="curl\lib\curlx\dynbuf.h" />
@ -280,6 +282,9 @@
<ClInclude Include="curl\lib\curlx\inet_pton.h" />
<ClInclude Include="curl\lib\curlx\multibyte.h" />
<ClInclude Include="curl\lib\curlx\nonblock.h" />
<ClInclude Include="curl\lib\curlx\snprintf.h" />
<ClInclude Include="curl\lib\curlx\strcopy.h" />
<ClInclude Include="curl\lib\curlx\strdup.h" />
<ClInclude Include="curl\lib\curlx\strerr.h" />
<ClInclude Include="curl\lib\curlx\strparse.h" />
<ClInclude Include="curl\lib\curlx\timediff.h" />
@ -300,9 +305,7 @@
<ClInclude Include="curl\lib\curl_ldap.h" />
<ClInclude Include="curl\lib\curl_md4.h" />
<ClInclude Include="curl\lib\curl_md5.h" />
<ClInclude Include="curl\lib\curl_memory.h" />
<ClInclude Include="curl\lib\curl_memrchr.h" />
<ClInclude Include="curl\lib\curl_mem_undef.h" />
<ClInclude Include="curl\lib\curl_ntlm_core.h" />
<ClInclude Include="curl\lib\curl_printf.h" />
<ClInclude Include="curl\lib\curl_range.h" />
@ -312,6 +315,7 @@
<ClInclude Include="curl\lib\curl_setup_once.h" />
<ClInclude Include="curl\lib\curl_sha256.h" />
<ClInclude Include="curl\lib\curl_sha512_256.h" />
<ClInclude Include="curl\lib\curl_share.h" />
<ClInclude Include="curl\lib\curl_sspi.h" />
<ClInclude Include="curl\lib\curl_threads.h" />
<ClInclude Include="curl\lib\curl_trc.h" />
@ -352,7 +356,6 @@
<ClInclude Include="curl\lib\imap.h" />
<ClInclude Include="curl\lib\llist.h" />
<ClInclude Include="curl\lib\macos.h" />
<ClInclude Include="curl\lib\memdebug.h" />
<ClInclude Include="curl\lib\mime.h" />
<ClInclude Include="curl\lib\mqtt.h" />
<ClInclude Include="curl\lib\multihandle.h" />
@ -367,7 +370,7 @@
<ClInclude Include="curl\lib\progress.h" />
<ClInclude Include="curl\lib\psl.h" />
<ClInclude Include="curl\lib\rand.h" />
<ClInclude Include="curl\lib\rename.h" />
<ClInclude Include="curl\lib\ratelimit.h" />
<ClInclude Include="curl\lib\request.h" />
<ClInclude Include="curl\lib\rtsp.h" />
<ClInclude Include="curl\lib\select.h" />
@ -376,7 +379,6 @@
<ClInclude Include="curl\lib\setup-os400.h" />
<ClInclude Include="curl\lib\setup-vms.h" />
<ClInclude Include="curl\lib\setup-win32.h" />
<ClInclude Include="curl\lib\share.h" />
<ClInclude Include="curl\lib\sigpipe.h" />
<ClInclude Include="curl\lib\slist.h" />
<ClInclude Include="curl\lib\smb.h" />
@ -384,7 +386,6 @@
<ClInclude Include="curl\lib\sockaddr.h" />
<ClInclude Include="curl\lib\socketpair.h" />
<ClInclude Include="curl\lib\socks.h" />
<ClInclude Include="curl\lib\speedcheck.h" />
<ClInclude Include="curl\lib\splay.h" />
<ClInclude Include="curl\lib\strcase.h" />
<ClInclude Include="curl\lib\strdup.h" />
@ -405,6 +406,7 @@
<ClInclude Include="curl\lib\vquic\curl_ngtcp2.h" />
<ClInclude Include="curl\lib\vquic\curl_quiche.h" />
<ClInclude Include="curl\lib\vquic\vquic_int.h" />
<ClInclude Include="curl\lib\vssh\vssh.h" />
<ClInclude Include="curl\lib\vtls\apple.h" />
<ClInclude Include="curl\lib\vtls\cipher_suite.h" />
<ClInclude Include="curl\lib\vtls\hostcheck.h" />
@ -418,12 +420,10 @@
<ClInclude Include="curl\lib\vauth\ntlm.h" />
<ClInclude Include="curl\lib\vauth\vauth.h" />
<ClInclude Include="curl\lib\vquic\vquic.h" />
<ClInclude Include="curl\lib\vssh\curl_path.h" />
<ClInclude Include="curl\lib\vssh\ssh.h" />
<ClInclude Include="curl\lib\vtls\gtls.h" />
<ClInclude Include="curl\lib\vtls\keylog.h" />
<ClInclude Include="curl\lib\vtls\mbedtls.h" />
<ClInclude Include="curl\lib\vtls\mbedtls_threadlock.h" />
<ClInclude Include="curl\lib\vtls\openssl.h" />
<ClInclude Include="curl\lib\vtls\schannel.h" />
<ClInclude Include="curl\lib\vtls\vtls.h" />

View File

@ -204,9 +204,6 @@
<ClCompile Include="curl\lib\rand.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\rename.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\rtsp.c">
<Filter>Source Files</Filter>
</ClCompile>
@ -222,9 +219,6 @@
<ClCompile Include="curl\lib\sha256.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\share.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\slist.c">
<Filter>Source Files</Filter>
</ClCompile>
@ -246,18 +240,12 @@
<ClCompile Include="curl\lib\socks_sspi.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\speedcheck.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\splay.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\strcase.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\strdup.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\strerror.c">
<Filter>Source Files</Filter>
</ClCompile>
@ -318,9 +306,6 @@
<ClCompile Include="curl\lib\vauth\vauth.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\vssh\curl_path.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\vssh\libssh.c">
<Filter>Source Files</Filter>
</ClCompile>
@ -333,9 +318,6 @@
<ClCompile Include="curl\lib\vtls\mbedtls.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\vtls\mbedtls_threadlock.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\vtls\openssl.c">
<Filter>Source Files</Filter>
</ClCompile>
@ -549,6 +531,27 @@
<ClCompile Include="curl\lib\curlx\strerr.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\curlx\strcopy.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\vssh\vssh.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\curl_share.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\ratelimit.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\curlx\basename.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\curlx\snprintf.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\curlx\strdup.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="curl\include\curl\curl.h">
@ -653,9 +656,6 @@
<ClInclude Include="curl\lib\curl_md5.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curl_memory.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curl_memrchr.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -758,9 +758,6 @@
<ClInclude Include="curl\lib\llist.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\memdebug.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\mime.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -791,9 +788,6 @@
<ClInclude Include="curl\lib\rand.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\rename.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\rtsp.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -812,9 +806,6 @@
<ClInclude Include="curl\lib\setup-vms.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\share.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\sigpipe.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -836,9 +827,6 @@
<ClInclude Include="curl\lib\socks.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\speedcheck.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\splay.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -887,9 +875,6 @@
<ClInclude Include="curl\lib\vauth\vauth.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\vssh\curl_path.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\vssh\ssh.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -899,9 +884,6 @@
<ClInclude Include="curl\lib\vtls\mbedtls.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\vtls\mbedtls_threadlock.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\vtls\openssl.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -1103,9 +1085,6 @@
<ClInclude Include="curl\lib\cf-ip-happy.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curl_mem_undef.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curl_fopen.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -1121,6 +1100,27 @@
<ClInclude Include="curl\lib\curlx\strerr.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curlx\snprintf.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curlx\strcopy.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\vssh\vssh.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curl_share.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\ratelimit.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curlx\basename.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curlx\strdup.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="curl\lib\libcurl.rc">

@ -1 +1 @@
Subproject commit a962f40bbba175e9716557a25d5d7965f134a3d3
Subproject commit 683181b47cfabd293e3ea409f838915b8297a4fd

@ -1 +1 @@
Subproject commit 05c44fcd18074836e21e1eda9fc02b3a4a1529b5
Subproject commit 51a5d623e3fde1f58829a56ba910f1cb33596222

2
3rdparty/zlib/zlib vendored

@ -1 +1 @@
Subproject commit 51b7f2abdade71cd9bb0e7a373ef2610ec6f9daf
Subproject commit da607da739fa6047df13e66a2af6b8bec7c2a498

View File

@ -13,12 +13,12 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11)
message(FATAL_ERROR "RPCS3 requires at least gcc-11.")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13)
message(FATAL_ERROR "RPCS3 requires at least gcc-13.")
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
message(FATAL_ERROR "RPCS3 requires at least clang-12.0.")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0)
message(FATAL_ERROR "RPCS3 requires at least clang-19.0.")
endif()
endif()

View File

@ -416,7 +416,7 @@ void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs)
out << YAML::BeginMap;
for (const auto& np : static_cast<const log_entry&>(rhs).get_map())
{
if (np.second == logs::level::notice) continue;
if (np.second == logs::level::_default) continue;
out << YAML::Key << np.first;
out << YAML::Value << fmt::format("%s", np.second);
}

View File

@ -117,6 +117,7 @@ static fs::error to_error(DWORD e)
case ERROR_NEGATIVE_SEEK: return fs::error::inval;
case ERROR_DIRECTORY: return fs::error::inval;
case ERROR_INVALID_NAME: return fs::error::inval;
case ERROR_INVALID_FUNCTION: return fs::error::inval;
case ERROR_SHARING_VIOLATION: return fs::error::acces;
case ERROR_DIR_NOT_EMPTY: return fs::error::notempty;
case ERROR_NOT_READY: return fs::error::noent;
@ -398,12 +399,11 @@ namespace fs
class windows_file final : public file_base
{
HANDLE m_handle;
atomic_t<u64> m_pos;
atomic_t<u64> m_pos {0};
public:
windows_file(HANDLE handle)
: m_handle(handle)
, m_pos(0)
{
}
@ -417,10 +417,10 @@ namespace fs
stat_t get_stat() override
{
FILE_BASIC_INFO basic_info;
FILE_BASIC_INFO basic_info {};
ensure(GetFileInformationByHandleEx(m_handle, FileBasicInfo, &basic_info, sizeof(FILE_BASIC_INFO))); // "file::stat"
stat_t info;
stat_t info {};
info.is_directory = (basic_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
info.is_writable = (basic_info.FileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
info.size = this->size();
@ -441,7 +441,7 @@ namespace fs
bool trunc(u64 length) override
{
FILE_END_OF_FILE_INFO _eof;
FILE_END_OF_FILE_INFO _eof {};
_eof.EndOfFile.QuadPart = length;
if (!SetFileInformationByHandle(m_handle, FileEndOfFileInfo, &_eof, sizeof(_eof)))
@ -563,6 +563,7 @@ namespace fs
u64 size() override
{
// NOTE: this can fail if we access a mounted empty drive (e.g. after unmounting an iso).
LARGE_INTEGER size;
ensure(GetFileSizeEx(m_handle, &size)); // "file::size"
@ -579,12 +580,12 @@ namespace fs
file_id id{"windows_file"};
id.data.resize(sizeof(FILE_ID_INFO));
FILE_ID_INFO info;
FILE_ID_INFO info {};
if (!GetFileInformationByHandleEx(m_handle, FileIdInfo, &info, sizeof(info)))
{
// Try GetFileInformationByHandle as a fallback
BY_HANDLE_FILE_INFORMATION info2;
BY_HANDLE_FILE_INFORMATION info2{};
ensure(GetFileInformationByHandle(m_handle, &info2));
info = {};
@ -625,7 +626,7 @@ namespace fs
struct ::stat file_info;
ensure(::fstat(m_fd, &file_info) == 0); // "file::stat"
stat_t info;
stat_t info {};
info.is_directory = S_ISDIR(file_info.st_mode);
info.is_writable = file_info.st_mode & 0200; // HACK: approximation
info.size = file_info.st_size;
@ -1656,6 +1657,45 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
return;
}
// Check if the handle is actually valid.
// This can fail on empty mounted drives (e.g. with ERROR_NOT_READY or ERROR_INVALID_FUNCTION).
BY_HANDLE_FILE_INFORMATION info{};
if (!GetFileInformationByHandle(handle, &info))
{
const DWORD last_error = GetLastError();
CloseHandle(handle);
if (last_error == ERROR_INVALID_FUNCTION)
{
g_tls_error = fs::error::isdir;
return;
}
g_tls_error = to_error(last_error);
return;
}
if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
CloseHandle(handle);
g_tls_error = fs::error::isdir;
return;
}
if (info.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
{
CloseHandle(handle);
g_tls_error = fs::error::acces;
return;
}
if ((mode & fs::write) && (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
{
CloseHandle(handle);
g_tls_error = fs::error::readonly;
return;
}
m_file = std::make_unique<windows_file>(handle);
#else
int flags = O_CLOEXEC; // Ensures all files are closed on execl for auto updater
@ -2595,7 +2635,7 @@ bool fs::pending_file::commit(bool overwrite)
while (file_handle != INVALID_HANDLE_VALUE)
{
// Get file ID (used to check for hardlinks)
BY_HANDLE_FILE_INFORMATION file_info;
BY_HANDLE_FILE_INFORMATION file_info{};
if (!GetFileInformationByHandle(file_handle, &file_info) || file_info.nNumberOfLinks == 1)
{

View File

@ -66,13 +66,13 @@ namespace fs
// File attributes (TODO)
struct stat_t
{
bool is_directory;
bool is_symlink;
bool is_writable;
u64 size;
s64 atime;
s64 mtime;
s64 ctime;
bool is_directory = false;
bool is_symlink = false;
bool is_writable = false;
u64 size = 0;
s64 atime = 0;
s64 mtime = 0;
s64 ctime = 0;
using enable_bitcopy = std::true_type;

View File

@ -14,6 +14,10 @@
#define CAN_OVERCOMMIT
#endif
#if defined(__APPLE__)
#include <mutex>
#endif
LOG_CHANNEL(jit_log, "JIT");
void jit_announce(uptr func, usz size, std::string_view name)

View File

@ -16,12 +16,12 @@
#include <errno.h>
#endif
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
#elif defined(__clang__)
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#elif defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4996)
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"

View File

@ -13,13 +13,13 @@ std::string wchar_to_utf8(std::wstring_view src);
std::string utf16_to_utf8(std::u16string_view src);
std::u16string utf8_to_utf16(std::string_view src);
// Copy null-terminated string from a std::string or a char array to a char array with truncation
template <typename D, typename T>
// Copy null-terminated string from a std::basic_string or a char array to a char array with truncation
template <typename D, typename T> requires requires (D& d, T& t) { std::declval<decltype(&t[0])&>() = &d[0]; }
inline void strcpy_trunc(D&& dst, const T& src)
{
const usz count = std::size(src) >= std::size(dst) ? std::max<usz>(std::size(dst), 1) - 1 : std::size(src);
std::memcpy(std::data(dst), std::data(src), count);
std::memset(std::data(dst) + count, 0, std::size(dst) - count);
std::copy_n(std::data(src), count, std::data(dst));
std::fill_n(std::data(dst) + count, std::size(dst) - count, std::remove_cvref_t<decltype(dst[0])>{});
}
// Convert string to signed integer

View File

@ -8,13 +8,17 @@
#include "Emu/RSX/RSXThread.h"
#include "Thread.h"
#include "Utilities/JIT.h"
#include <thread>
#include <cfenv>
#ifdef ARCH_ARM64
#include "Emu/CPU/Backends/AArch64/AArch64Signal.h"
#endif
#ifdef __cpp_lib_stacktrace
#include "rpcs3_version.h"
#include <stacktrace>
#endif
#ifdef _WIN32
#include <Windows.h>
#include <Psapi.h>
@ -2801,6 +2805,16 @@ void thread_base::exec()
[[noreturn]] void thread_ctrl::emergency_exit(std::string_view reason)
{
// Print stacktrace
#ifdef __cpp_lib_stacktrace
if (rpcs3::is_local_build())
{
std::ostringstream oss;
oss << std::stacktrace::current();
sys_log.notice("StackTrace\n\n%s\n", oss.str());
}
#endif
if (const std::string info = dump_useful_thread_info(); !info.empty())
{
sys_log.notice("\n%s", info);

View File

@ -4,6 +4,7 @@
#include "util/atomic.hpp"
#include "util/shared_ptr.hpp"
#include <thread>
#include <string>
// Hardware core layout

View File

@ -329,7 +329,7 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
is_valid = false;
continue;
}
else if (serial.size() != 9 || !std::all_of(serial.begin(), serial.end(), [](char c) { return std::isalnum(c); }))
else if (serial.size() != 9 || !std::all_of(serial.begin(), serial.end(), [](char c) { return std::isalnum(static_cast<unsigned char>(c)); }))
{
append_log_message(log_messages, fmt::format("Error: Serial '%s' invalid (patch: %s, key: %s, location: %s, file: %s)", serial, description, main_key, get_yaml_node_location(serial_node), path), &patch_log.error);
is_valid = false;

View File

@ -821,6 +821,14 @@ struct color4_base
a *= rhs;
}
void operator += (const color4_base<T>& rhs)
{
r += rhs.r;
g += rhs.g;
b += rhs.b;
a += rhs.a;
}
constexpr color4_base<T> operator * (const color4_base<T>& rhs) const
{
return { r * rhs.r, g * rhs.g, b * rhs.b, a * rhs.a };

View File

@ -13,7 +13,7 @@
<ItemDefinitionGroup>
<ClCompile>
<LanguageStandard Condition = "'$(VisualStudioVersion.Substring(0,2))'&lt;'17'">stdcpplatest</LanguageStandard>
<LanguageStandard Condition = "'$(VisualStudioVersion.Substring(0,2))'&gt;='17'">stdcpp20</LanguageStandard>
<LanguageStandard Condition = "'$(VisualStudioVersion.Substring(0,2))'&gt;='17'">stdcpp23</LanguageStandard>
<PreprocessorDefinitions>_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING=1;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ExceptionHandling>false</ExceptionHandling>
<AdditionalOptions>-d2FH4- %(AdditionalOptions)</AdditionalOptions>

View File

@ -8,7 +8,7 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/git-version.cmake)
include(ConfigureCompiler)
include(CheckFunctionExists)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 23)
if(UNIX AND NOT APPLE AND NOT ANDROID)
add_compile_definitions(DATADIR="${CMAKE_INSTALL_FULL_DATADIR}/rpcs3")
@ -195,6 +195,7 @@ if(BUILD_RPCS3_TESTS)
tests/test_address_range.cpp
tests/test_rsx_cfg.cpp
tests/test_rsx_fp_asm.cpp
tests/test_dmux_pamf.cpp
)
target_link_libraries(rpcs3_test
@ -202,6 +203,7 @@ if(BUILD_RPCS3_TESTS)
rpcs3_lib
rpcs3_emu
GTest::gtest
GTest::gmock
)
target_include_directories(rpcs3_test

View File

@ -501,6 +501,7 @@ target_sources(rpcs3_emu PRIVATE
RSX/Overlays/overlays.cpp
RSX/Overlays/overlay_animated_icon.cpp
RSX/Overlays/overlay_animation.cpp
RSX/Overlays/overlay_audio.cpp
RSX/Overlays/overlay_compile_notification.cpp
RSX/Overlays/overlay_controls.cpp
RSX/Overlays/overlay_cursor.cpp

View File

@ -20,19 +20,19 @@ namespace aarch64
sp
};
static const char* gpr_names[] =
[[maybe_unused]] static const char* gpr_names[] =
{
"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9",
"x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "x19",
"x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "x29", "x30"
};
static const char* spr_names[] =
[[maybe_unused]] static const char* spr_names[] =
{
"xzr", "pc", "sp"
};
static const char* spr_asm_names[] =
[[maybe_unused]] static const char* spr_asm_names[] =
{
"xzr", ".", "sp"
};

View File

@ -206,11 +206,7 @@ struct cpu_prof
// Print only 7 hash characters out of 11 (which covers roughly 48 bits)
if (type_id == 2)
{
fmt::append(results, "\n\t[%s", fmt::base57(be_t<u64>{name}));
results.resize(results.size() - 4);
// Print chunk address from lowest 16 bits
fmt::append(results, "...chunk-0x%05x]: %.4f%% (%u)", (name & 0xffff) * 4, _frac * 100., count);
fmt::append(results, "\n\t[%s]: %.4f%% (%u)", spu_block_hash{name}, _frac * 100., count);
}
else
{
@ -1377,7 +1373,7 @@ std::vector<std::pair<u32, u32>> cpu_thread::dump_callstack_list() const
std::string cpu_thread::dump_misc() const
{
return fmt::format("Type: %s; State: %s\n", get_class() == thread_class::ppu ? "PPU" : get_class() == thread_class::spu ? "SPU" : "RSX", state.load());
return fmt::format("%s[0x%x]; State: %s\n", get_class() == thread_class::ppu ? "PPU" : get_class() == thread_class::spu ? "SPU" : "RSX", id, state.load());
}
bool cpu_thread::suspend_work::push(cpu_thread* _this) noexcept

View File

@ -201,6 +201,13 @@ void cpu_translator::initialize(llvm::LLVMContext& context, llvm::ExecutionEngin
m_use_vnni = true;
m_use_gfni = true;
}
#ifdef ARCH_ARM64
if (utils::has_dotprod())
{
m_use_dotprod = true;
}
#endif
}
llvm::Value* cpu_translator::bitcast(llvm::Value* val, llvm::Type* type) const

View File

@ -3090,6 +3090,9 @@ protected:
// For now, setting this flag will speed up SPU verification
// but I will remove this later with explicit parralelism - Whatcookie
bool m_use_avx = true;
// ARMv8 SDOT/UDOT
bool m_use_dotprod = false;
#else
// Allow FMA
bool m_use_fma = false;
@ -3647,10 +3650,59 @@ public:
const auto data0 = a.eval(m_ir);
const auto data1 = b.eval(m_ir);
const auto data2 = c.eval(m_ir);
#if LLVM_VERSION_MAJOR >= 22
// LLVM 22+ changed the intrinsic signature from v4i32 to v16i8 for operands 2 and 3
result.value = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_avx512_vpdpbusd_128),
{data0, m_ir->CreateBitCast(data1, get_type<u8[16]>()), m_ir->CreateBitCast(data2, get_type<u8[16]>())});
#else
result.value = m_ir->CreateCall(get_intrinsic(llvm::Intrinsic::x86_avx512_vpdpbusd_128), {data0, data1, data2});
#endif
return result;
}
#ifdef ARCH_ARM64
template <typename T1, typename T2, typename T3>
value_t<u32[4]> udot(T1 a, T2 b, T3 c)
{
value_t<u32[4]> result;
const auto data0 = a.eval(m_ir);
const auto data1 = b.eval(m_ir);
const auto data2 = c.eval(m_ir);
result.value = m_ir->CreateCall(get_intrinsic<u32[4], u8[16]>(llvm::Intrinsic::aarch64_neon_udot), {data0, data1, data2});
return result;
}
template <typename T1, typename T2, typename T3>
value_t<u32[4]> sdot(T1 a, T2 b, T3 c)
{
value_t<u32[4]> result;
const auto data0 = a.eval(m_ir);
const auto data1 = b.eval(m_ir);
const auto data2 = c.eval(m_ir);
result.value = m_ir->CreateCall(get_intrinsic<u32[4], u8[16]>(llvm::Intrinsic::aarch64_neon_sdot), {data0, data1, data2});
return result;
}
template <typename T1, typename T2>
auto addp(T1 a, T2 b)
{
using T_vector = typename is_llvm_expr<T1>::type;
const auto data1 = a.eval(m_ir);
const auto data2 = b.eval(m_ir);
const auto func = get_intrinsic<T_vector>(llvm::Intrinsic::aarch64_neon_addp);
value_t<T_vector> result;
result.value = m_ir->CreateCall(func, {data1, data2});
return result;
}
#endif
template <typename T1, typename T2>
value_t<u8[16]> vpermb(T1 a, T2 b)
{

View File

@ -919,7 +919,7 @@ error_code cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr<u32> arg1, vm
if (!check_dev_num(dev_num))
{
return CELL_CAMERA_ERROR_PARAM;
return { CELL_CAMERA_ERROR_PARAM, "dev_num=%d", dev_num };
}
if (g_cfg.io.camera == camera_handler::null)
@ -935,7 +935,7 @@ error_code cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr<u32> arg1, vm
if (!arg1)
{
return CELL_CAMERA_ERROR_PARAM;
return { CELL_CAMERA_ERROR_PARAM, "arg1=null" };
}
if (error_code error = check_resolution(dev_num))
@ -952,7 +952,7 @@ error_code cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr<u32> arg1, vm
if (!attr_name) // invalid attributes don't have a name
{
return CELL_CAMERA_ERROR_PARAM;
return { CELL_CAMERA_ERROR_PARAM, "attrib=0x%x", attrib };
}
if (arg1)
@ -983,7 +983,7 @@ error_code cellCameraSetAttribute(s32 dev_num, s32 attrib, u32 arg1, u32 arg2)
if (!check_dev_num(dev_num))
{
return CELL_CAMERA_ERROR_PARAM;
return { CELL_CAMERA_ERROR_PARAM, "dev_num=%d", dev_num };
}
if (g_cfg.io.camera == camera_handler::null)
@ -1004,7 +1004,7 @@ error_code cellCameraSetAttribute(s32 dev_num, s32 attrib, u32 arg1, u32 arg2)
if (!attr_name) // invalid attributes don't have a name
{
return CELL_CAMERA_ERROR_PARAM;
return { CELL_CAMERA_ERROR_PARAM, "attrib=0x%x", attrib };
}
g_camera.set_attr(attrib, arg1, arg2);

View File

@ -169,18 +169,18 @@ public:
static const u32 id_count = 1023;
SAVESTATE_INIT_POS(34);
ElementaryStream(Demuxer* dmux, u32 addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, vm::ptr<CellDmuxCbEsMsg> cbFunc, u32 cbArg, u32 spec);
ElementaryStream(Demuxer* dmux, vm::ptr<void> addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, vm::ptr<CellDmuxCbEsMsg> cbFunc, vm::ptr<void> cbArg, u32 spec);
Demuxer* dmux;
const u32 id = idm::last_id();
const u32 memAddr;
const vm::ptr<void> memAddr;
const u32 memSize;
const u32 fidMajor;
const u32 fidMinor;
const u32 sup1;
const u32 sup2;
const vm::ptr<CellDmuxCbEsMsg> cbFunc;
const u32 cbArg;
const vm::ptr<void> cbArg;
const u32 spec; //addr
std::vector<u8> raw_data; // demultiplexed data stream (managed by demuxer thread)
@ -208,13 +208,13 @@ public:
const u32 memAddr;
const u32 memSize;
const vm::ptr<CellDmuxCbMsg> cbFunc;
const u32 cbArg;
const vm::ptr<void> cbArg;
volatile bool is_finished = false;
volatile bool is_closed = false;
atomic_t<bool> is_running = false;
atomic_t<bool> is_working = false;
Demuxer(u32 addr, u32 size, vm::ptr<CellDmuxCbMsg> func, u32 arg)
Demuxer(u32 addr, u32 size, vm::ptr<CellDmuxCbMsg> func, vm::ptr<void> arg)
: ppu_thread({}, "", 0)
, memAddr(addr)
, memSize(size)
@ -755,11 +755,11 @@ PesHeader::PesHeader(DemuxerStream& stream)
is_ok = true;
}
ElementaryStream::ElementaryStream(Demuxer* dmux, u32 addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, vm::ptr<CellDmuxCbEsMsg> cbFunc, u32 cbArg, u32 spec)
: put(utils::align(addr, 128))
ElementaryStream::ElementaryStream(Demuxer* dmux, vm::ptr<void> addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, vm::ptr<CellDmuxCbEsMsg> cbFunc, vm::ptr<void> cbArg, u32 spec)
: put(utils::align(addr.addr(), 128))
, dmux(dmux)
, memAddr(utils::align(addr, 128))
, memSize(size - (addr - memAddr))
, memAddr(vm::ptr<void>::make(utils::align(addr.addr(), 128)))
, memSize(size - (addr.addr() - memAddr.addr()))
, fidMajor(fidMajor)
, fidMinor(fidMinor)
, sup1(sup1)
@ -788,9 +788,9 @@ bool ElementaryStream::is_full(u32 space)
{
return first - put < space + 128;
}
else if (put + space + 128 > memAddr + memSize)
else if (put + space + 128 > memAddr.addr() + memSize)
{
return first - memAddr < space + 128;
return first - memAddr.addr() < space + 128;
}
else
{
@ -816,35 +816,35 @@ void ElementaryStream::push_au(u32 size, u64 dts, u64 pts, u64 userdata, bool ra
std::lock_guard lock(m_mutex);
ensure(!is_full(size));
if (put + size + 128 > memAddr + memSize)
if (put + size + 128 > memAddr.addr() + memSize)
{
put = memAddr;
put = memAddr.addr();
}
std::memcpy(vm::base(put + 128), raw_data.data(), size);
raw_data.erase(raw_data.begin(), raw_data.begin() + size);
auto info = vm::ptr<CellDmuxAuInfoEx>::make(put);
info->auAddr = put + 128;
info->auAddr.set(put + 128);
info->auSize = size;
info->dts.lower = static_cast<u32>(dts);
info->dts.upper = static_cast<u32>(dts >> 32);
info->pts.lower = static_cast<u32>(pts);
info->pts.upper = static_cast<u32>(pts >> 32);
info->isRap = rap;
info->reserved = 0;
info->auMaxSize = 0;
info->userData = userdata;
auto spec = vm::ptr<u32>::make(put + u32{sizeof(CellDmuxAuInfoEx)});
*spec = specific;
auto inf = vm::ptr<CellDmuxAuInfo>::make(put + 64);
inf->auAddr = put + 128;
inf->auAddr.set(put + 128);
inf->auSize = size;
inf->dtsLower = static_cast<u32>(dts);
inf->dtsUpper = static_cast<u32>(dts >> 32);
inf->ptsLower = static_cast<u32>(pts);
inf->ptsUpper = static_cast<u32>(pts >> 32);
inf->dts.lower = static_cast<u32>(dts);
inf->dts.upper = static_cast<u32>(dts >> 32);
inf->pts.lower = static_cast<u32>(pts);
inf->pts.upper = static_cast<u32>(pts >> 32);
inf->auMaxSize = 0; // ?????
inf->userData = userdata;
@ -927,7 +927,7 @@ bool ElementaryStream::peek(u32& out_data, bool no_ex, u32& out_spec, bool updat
void ElementaryStream::reset()
{
std::lock_guard lock(m_mutex);
put = memAddr;
put = memAddr.addr();
entries.clear();
put_count = 0;
got_count = 0;

View File

@ -33,118 +33,6 @@ enum CellDmuxEsMsgType : s32
CELL_DMUX_ES_MSG_TYPE_FLUSH_DONE = 1,
};
enum CellDmuxPamfM2vLevel : s32
{
CELL_DMUX_PAMF_M2V_MP_LL = 0,
CELL_DMUX_PAMF_M2V_MP_ML,
CELL_DMUX_PAMF_M2V_MP_H14,
CELL_DMUX_PAMF_M2V_MP_HL,
};
enum CellDmuxPamfAvcLevel : s32
{
CELL_DMUX_PAMF_AVC_LEVEL_2P1 = 21,
CELL_DMUX_PAMF_AVC_LEVEL_3P0 = 30,
CELL_DMUX_PAMF_AVC_LEVEL_3P1 = 31,
CELL_DMUX_PAMF_AVC_LEVEL_3P2 = 32,
CELL_DMUX_PAMF_AVC_LEVEL_4P1 = 41,
CELL_DMUX_PAMF_AVC_LEVEL_4P2 = 42,
};
struct CellDmuxPamfAuSpecificInfoM2v
{
be_t<u32> reserved1;
};
struct CellDmuxPamfAuSpecificInfoAvc
{
be_t<u32> reserved1;
};
struct CellDmuxPamfAuSpecificInfoLpcm
{
u8 channelAssignmentInfo;
u8 samplingFreqInfo;
u8 bitsPerSample;
};
struct CellDmuxPamfAuSpecificInfoAc3
{
be_t<u32> reserved1;
};
struct CellDmuxPamfAuSpecificInfoAtrac3plus
{
be_t<u32> reserved1;
};
struct CellDmuxPamfAuSpecificInfoUserData
{
be_t<u32> reserved1;
};
struct CellDmuxPamfEsSpecificInfoM2v
{
be_t<u32> profileLevel;
};
struct CellDmuxPamfEsSpecificInfoAvc
{
be_t<u32> level;
};
struct CellDmuxPamfEsSpecificInfoLpcm
{
be_t<u32> samplingFreq;
be_t<u32> numOfChannels;
be_t<u32> bitsPerSample;
};
struct CellDmuxPamfEsSpecificInfoAc3
{
be_t<u32> reserved1;
};
struct CellDmuxPamfEsSpecificInfoAtrac3plus
{
be_t<u32> reserved1;
};
struct CellDmuxPamfEsSpecificInfoUserData
{
be_t<u32> reserved1;
};
enum CellDmuxPamfSamplingFrequency : s32
{
CELL_DMUX_PAMF_FS_48K = 48000,
};
enum CellDmuxPamfBitsPerSample : s32
{
CELL_DMUX_PAMF_BITS_PER_SAMPLE_16 = 16,
CELL_DMUX_PAMF_BITS_PER_SAMPLE_24 = 24,
};
enum CellDmuxPamfLpcmChannelAssignmentInfo : s32
{
CELL_DMUX_PAMF_LPCM_CH_M1 = 1,
CELL_DMUX_PAMF_LPCM_CH_LR = 3,
CELL_DMUX_PAMF_LPCM_CH_LRCLSRSLFE = 9,
CELL_DMUX_PAMF_LPCM_CH_LRCLSCS1CS2RSLFE = 11,
};
enum CellDmuxPamfLpcmFs : s32
{
CELL_DMUX_PAMF_LPCM_FS_48K = 1,
};
enum CellDmuxPamfLpcmBitsPerSamples : s32
{
CELL_DMUX_PAMF_LPCM_BITS_PER_SAMPLE_16 = 1,
CELL_DMUX_PAMF_LPCM_BITS_PER_SAMPLE_24 = 3,
};
struct CellDmuxMsg
{
be_t<s32> msgType; // CellDmuxMsgType
@ -163,12 +51,6 @@ struct CellDmuxType
be_t<u32> reserved[2];
};
struct CellDmuxPamfSpecificInfo
{
be_t<u32> thisSize;
b8 programEndCodeCb;
};
struct CellDmuxType2
{
be_t<s32> streamType; // CellDmuxStreamType
@ -177,7 +59,7 @@ struct CellDmuxType2
struct CellDmuxResource
{
be_t<u32> memAddr;
vm::bptr<void> memAddr;
be_t<u32> memSize;
be_t<u32> ppuThreadPriority;
be_t<u32> ppuThreadStackSize;
@ -187,7 +69,7 @@ struct CellDmuxResource
struct CellDmuxResourceEx
{
be_t<u32> memAddr;
vm::bptr<void> memAddr;
be_t<u32> memSize;
be_t<u32> ppuThreadPriority;
be_t<u32> ppuThreadStackSize;
@ -227,16 +109,16 @@ struct CellDmuxResource2
be_t<u32> shit[4];
};
using CellDmuxCbMsg = u32(u32 demuxerHandle, vm::ptr<CellDmuxMsg> demuxerMsg, u32 cbArg);
using CellDmuxCbMsg = u32(u32 demuxerHandle, vm::cptr<CellDmuxMsg> demuxerMsg, vm::ptr<void> cbArg);
using CellDmuxCbEsMsg = u32(u32 demuxerHandle, u32 esHandle, vm::ptr<CellDmuxEsMsg> esMsg, u32 cbArg);
using CellDmuxCbEsMsg = u32(u32 demuxerHandle, u32 esHandle, vm::cptr<CellDmuxEsMsg> esMsg, vm::ptr<void> cbArg);
// Used for internal callbacks as well
template <typename F>
struct DmuxCb
{
vm::bptr<F> cbFunc;
be_t<u32> cbArg;
vm::bptr<void> cbArg;
};
using CellDmuxCb = DmuxCb<CellDmuxCbMsg>;
@ -250,42 +132,50 @@ struct CellDmuxAttr
be_t<u32> demuxerVerLower;
};
struct CellDmuxPamfAttr
{
be_t<u32> maxEnabledEsNum;
be_t<u32> version;
be_t<u32> memSize;
};
struct CellDmuxEsAttr
{
be_t<u32> memSize;
};
struct CellDmuxPamfEsAttr
{
be_t<u32> auQueueMaxSize;
be_t<u32> memSize;
be_t<u32> specificInfoSize;
};
struct CellDmuxEsResource
{
be_t<u32> memAddr;
vm::bptr<void> memAddr;
be_t<u32> memSize;
};
struct CellDmuxAuInfo
{
be_t<u32> auAddr;
vm::bptr<void> auAddr;
be_t<u32> auSize;
be_t<u32> auMaxSize;
be_t<u64> userData;
be_t<u32> ptsUpper;
be_t<u32> ptsLower;
be_t<u32> dtsUpper;
be_t<u32> dtsLower;
};
struct CellDmuxAuInfoEx
{
be_t<u32> auAddr;
be_t<u32> auSize;
be_t<u32> reserved;
b8 isRap;
be_t<u64> userData;
CellCodecTimeStamp pts;
CellCodecTimeStamp dts;
};
struct CellDmuxPamfAttr;
struct CellDmuxPamfEsAttr;
using CellDmuxAuInfoEx = CellDmuxAuInfo;
struct DmuxAuInfo
{
CellDmuxAuInfo info;
vm::bptr<void> specific_info;
be_t<u32> specific_info_size;
};
using DmuxNotifyDemuxDone = error_code(vm::ptr<void>, u32, vm::ptr<void>);
using DmuxNotifyFatalErr = error_code(vm::ptr<void>, u32, vm::ptr<void>);
@ -301,7 +191,7 @@ using CellDmuxCoreOpResetStream = error_code(vm::ptr<void>);
using CellDmuxCoreOpCreateThread = error_code(vm::ptr<void>);
using CellDmuxCoreOpJoinThread = error_code(vm::ptr<void>);
using CellDmuxCoreOpSetStream = error_code(vm::ptr<void>, vm::cptr<void>, u32, b8, u64);
using CellDmuxCoreOpFreeMemory = error_code(vm::ptr<void>, vm::ptr<void>, u32);
using CellDmuxCoreOpReleaseAu = error_code(vm::ptr<void>, vm::ptr<void>, u32);
using CellDmuxCoreOpQueryEsAttr = error_code(vm::cptr<void>, vm::cptr<void>, vm::ptr<CellDmuxPamfEsAttr>);
using CellDmuxCoreOpEnableEs = error_code(vm::ptr<void>, vm::cptr<void>, vm::cptr<CellDmuxEsResource>, vm::cptr<DmuxCb<DmuxEsNotifyAuFound>>, vm::cptr<DmuxCb<DmuxEsNotifyFlushDone>>, vm::cptr<void>, vm::pptr<void>);
using CellDmuxCoreOpDisableEs = u32(vm::ptr<void>);
@ -318,7 +208,7 @@ struct CellDmuxCoreOps
vm::bptr<CellDmuxCoreOpCreateThread> createThread;
vm::bptr<CellDmuxCoreOpJoinThread> joinThread;
vm::bptr<CellDmuxCoreOpSetStream> setStream;
vm::bptr<CellDmuxCoreOpFreeMemory> freeMemory;
vm::bptr<CellDmuxCoreOpReleaseAu> releaseAu;
vm::bptr<CellDmuxCoreOpQueryEsAttr> queryEsAttr;
vm::bptr<CellDmuxCoreOpEnableEs> enableEs;
vm::bptr<CellDmuxCoreOpDisableEs> disableEs;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -492,8 +492,8 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr<char> dirName
strcpy_trunc(get->getParam.titleLang[i], psf::get_string(psf, fmt::format("TITLE_%02d", i)));
}
cellGame.warning("cellHddGameCheck(): Data exists:\nATTRIBUTE: 0x%x, RESOLUTION: 0x%x, RESOLUTION: 0x%x, SOUND_FORMAT: 0x%x, dataVersion: %s"
, get->getParam.attribute, get->getParam.resolution, get->getParam.soundFormat, get->getParam.soundFormat, std::span<const u8>(reinterpret_cast<const u8*>(get->getParam.dataVersion), 6));
cellGame.warning("cellHddGameCheck(): Data exists:\nATTRIBUTE: 0x%x, RESOLUTION: 0x%x, SOUND_FORMAT: 0x%x, dataVersion: %s"
, get->getParam.attribute, get->getParam.resolution, get->getParam.soundFormat, std::span<const u8>(reinterpret_cast<const u8*>(get->getParam.dataVersion), 6));
}
// TODO ?
@ -580,7 +580,7 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr<char> dirName
break;
default:
cellGame.error("cellHddGameCheck(): callback returned unknown error (code=0x%x). Error message: %s", result->invalidMsg);
cellGame.error("cellHddGameCheck(): callback returned unknown error (code=0x%x). Error message: %s", result->result, result->invalidMsg);
error_msg = get_localized_string(localized_string_id::CELL_HDD_GAME_CHECK_INVALID, "%s", result->invalidMsg);
break;
}
@ -1199,7 +1199,7 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
break;
default:
cellGame.error("cellGameDataCheckCreate2(): callback returned unknown error (code=0x%x). Error message: %s", cbResult->invalidMsg);
cellGame.error("cellGameDataCheckCreate2(): callback returned unknown error (code=0x%x). Error message: %s", cbResult->result, cbResult->invalidMsg);
error_msg = get_localized_string(localized_string_id::CELL_GAMEDATA_CHECK_INVALID, "%s", cbResult->invalidMsg);
break;
}
@ -1747,7 +1747,7 @@ error_code cellGameThemeInstall(vm::cptr<char> usrdirPath, vm::cptr<char> fileNa
{
u32 magic{};
if (src_path.ends_with(".p3t") || !theme.read(magic) || magic != "P3TF"_u32)
if (!fmt::to_lower(src_path).ends_with(".p3t") || !theme.read(magic) || magic != "P3TF"_u32)
{
return CELL_GAME_ERROR_INVALID_THEME_FILE;
}
@ -1819,7 +1819,7 @@ error_code cellGameThemeInstallFromBuffer(ppu_thread& ppu, u32 fileSize, u32 buf
const u32 read_size = std::min(bufSize, fileSize - file_offset);
cellGame.notice("cellGameThemeInstallFromBuffer: writing %d bytes at pos %d", read_size, file_offset);
if (theme.write(reinterpret_cast<u8*>(buf.get_ptr()) + file_offset, read_size) != read_size)
if (theme.write(reinterpret_cast<u8*>(buf.get_ptr()), read_size) != read_size)
{
cellGame.error("cellGameThemeInstallFromBuffer: failed to write to destination file '%s' (error=%s)", dst_path, fs::g_tls_error);

View File

@ -770,8 +770,8 @@ namespace gem
if constexpr (use_gain)
{
dst0[0] = static_cast<u8>(std::clamp(r * gain_r, 0.0f, 255.0f));
dst0[1] = static_cast<u8>(std::clamp(b * gain_b, 0.0f, 255.0f));
dst0[2] = static_cast<u8>(std::clamp(g * gain_g, 0.0f, 255.0f));
dst0[1] = static_cast<u8>(std::clamp(g * gain_g, 0.0f, 255.0f));
dst0[2] = static_cast<u8>(std::clamp(b * gain_b, 0.0f, 255.0f));
}
else
{
@ -822,8 +822,8 @@ namespace gem
if constexpr (use_gain)
{
dst0[0] = static_cast<u8>(std::clamp(r * gain_r, 0.0f, 255.0f));
dst0[1] = static_cast<u8>(std::clamp(b * gain_b, 0.0f, 255.0f));
dst0[2] = static_cast<u8>(std::clamp(g * gain_g, 0.0f, 255.0f));
dst0[1] = static_cast<u8>(std::clamp(g * gain_g, 0.0f, 255.0f));
dst0[2] = static_cast<u8>(std::clamp(b * gain_b, 0.0f, 255.0f));
}
else
{
@ -845,6 +845,53 @@ namespace gem
debayer_raw8_impl<false>(src, dst, alpha, gain_r, gain_g, gain_b);
}
template <bool use_gain>
static inline void debayer_raw8_downscale_impl(const u8* src, u8* dst, u8 alpha, f32 gain_r, f32 gain_g, f32 gain_b)
{
constexpr u32 in_pitch = 640;
constexpr u32 out_pitch = 320 * 4;
// Simple debayer
for (s32 y = 0; y < 240; y++)
{
const u8* src0 = src + y * 2 * in_pitch;
const u8* src1 = src0 + in_pitch;
u8* dst0 = dst + y * out_pitch;
for (s32 x = 0; x < 320; x++, dst0 += 4, src0 += 2, src1 += 2)
{
const u8 b = src0[0];
const u8 g0 = src0[1];
const u8 g1 = src1[0];
const u8 r = src1[1];
const u8 g = (g0 + g1) >> 1;
if constexpr (use_gain)
{
dst0[0] = static_cast<u8>(std::clamp(r * gain_r, 0.0f, 255.0f));
dst0[1] = static_cast<u8>(std::clamp(g * gain_g, 0.0f, 255.0f));
dst0[2] = static_cast<u8>(std::clamp(b * gain_b, 0.0f, 255.0f));
}
else
{
dst0[0] = r;
dst0[1] = g;
dst0[2] = b;
}
dst0[3] = alpha;
}
}
}
static void debayer_raw8_downscale(const u8* src, u8* dst, u8 alpha, f32 gain_r, f32 gain_g, f32 gain_b)
{
if (gain_r != 1.0f || gain_g != 1.0f || gain_b != 1.0f)
debayer_raw8_downscale_impl<true>(src, dst, alpha, gain_r, gain_g, gain_b);
else
debayer_raw8_downscale_impl<false>(src, dst, alpha, gain_r, gain_g, gain_b);
}
bool convert_image_format(CellCameraFormat input_format, const CellGemVideoConvertAttribute& vc,
const std::vector<u8>& video_data_in, u32 width, u32 height,
u8* video_data_out, u32 video_data_out_size, u8* buffer_memory,
@ -881,9 +928,9 @@ namespace gem
const u8* src_data = video_data_in.data();
const u8 alpha = vc.alpha;
const f32 gain_r = vc.gain * vc.blue_gain;
const f32 gain_r = vc.gain * vc.red_gain;
const f32 gain_g = vc.gain * vc.green_gain;
const f32 gain_b = vc.gain * vc.red_gain;
const f32 gain_b = vc.gain * vc.blue_gain;
// Only RAW8 should be relevant for cellGem unless I'm mistaken
if (input_format == CELL_CAMERA_RAW8)
@ -1183,34 +1230,7 @@ namespace gem
{
case CELL_CAMERA_RAW8:
{
const u32 in_pitch = width;
const u32 out_pitch = width * 4 / 2;
for (u32 y = 0; y < height - 1; y += 2)
{
const u8* src0 = src_data + y * in_pitch;
const u8* src1 = src0 + in_pitch;
u8* dst0 = video_data_out + (y / 2) * out_pitch;
u8* dst1 = dst0 + out_pitch;
for (u32 x = 0; x < width - 1; x += 2, src0 += 2, src1 += 2, dst0 += 4, dst1 += 4)
{
const u8 b = src0[0];
const u8 g0 = src0[1];
const u8 g1 = src1[0];
const u8 r = src1[1];
const u8 top[4] = { r, g0, b, alpha };
const u8 bottom[4] = { r, g1, b, alpha };
// Top-Left
std::memcpy(dst0, top, 4);
// Bottom-Left Pixel
std::memcpy(dst1, bottom, 4);
}
}
debayer_raw8_downscale(src_data, video_data_out, alpha, gain_r, gain_g, gain_b);
break;
}
case CELL_CAMERA_RGBA:
@ -1609,13 +1629,8 @@ public:
return false;
}
if (!m_camera_info.bytesize)
{
cellGem.error("gem_tracker: unexpected image size: %d", m_camera_info.bytesize);
return false;
}
m_tracker.set_image_data(m_camera_info.buffer.get_ptr(), m_camera_info.bytesize, m_camera_info.width, m_camera_info.height, m_camera_info.format);
m_framenumber++; // using framenumber instead of timestamp since the timestamp could be identical
return true;
}
@ -1648,6 +1663,7 @@ public:
}
auto& gem = g_fxo->get<gem_config>();
u64 last_framenumber = 0;
while (thread_ctrl::state() != thread_state::aborting)
{
@ -1663,6 +1679,13 @@ public:
}
}
if (std::exchange(last_framenumber, m_framenumber.load()) == last_framenumber)
{
cellGem.warning("Tracker woke up without new frame. Skipping processing (framenumber=%d)", last_framenumber);
tracker_done();
continue;
}
m_busy.release(true);
// Update PS Move LED colors
@ -1754,6 +1777,7 @@ public:
private:
atomic_t<u32> m_wake_up_tracker = 0;
atomic_t<u32> m_tracker_done = 0;
atomic_t<u64> m_framenumber = 0;
atomic_t<bool> m_busy = false;
ps_move_tracker<false> m_tracker{};
CellCameraInfoEx m_camera_info{};
@ -1873,21 +1897,10 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con
gem_state->pos[2] = controller.distance_mm;
gem_state->pos[3] = 0.f;
// TODO: calculate handle position based on our world coordinate and the angles
gem_state->handle_pos[0] = camera_x;
gem_state->handle_pos[1] = camera_y;
gem_state->handle_pos[2] = controller.distance_mm + 10.0f;
gem_state->handle_pos[3] = 0.f;
// Calculate orientation
if (g_cfg.io.move == move_handler::real || (g_cfg.io.move == move_handler::fake && move_data.orientation_enabled))
{
gem_state->quat[0] = move_data.quaternion.x();
gem_state->quat[1] = move_data.quaternion.y();
gem_state->quat[2] = move_data.quaternion.z();
gem_state->quat[3] = move_data.quaternion.w();
}
else
ps_move_data::vect<4> quat = move_data.quaternion;
if (g_cfg.io.move != move_handler::real && !(g_cfg.io.move == move_handler::fake && move_data.orientation_enabled))
{
const f32 max_angle_per_side_h = g_cfg.io.fake_move_rotation_cone_h / 2.0f;
const f32 max_angle_per_side_v = g_cfg.io.fake_move_rotation_cone_v / 2.0f;
@ -1901,17 +1914,27 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con
const f32 cy = std::cos(yaw * 0.5f);
const f32 sy = std::sin(yaw * 0.5f);
const f32 q_x = sr * cp * cy - cr * sp * sy;
const f32 q_y = cr * sp * cy + sr * cp * sy;
const f32 q_z = cr * cp * sy - sr * sp * cy;
const f32 q_w = cr * cp * cy + sr * sp * sy;
gem_state->quat[0] = q_x;
gem_state->quat[1] = q_y;
gem_state->quat[2] = q_z;
gem_state->quat[3] = q_w;
quat.x() = sr * cp * cy - cr * sp * sy;
quat.y() = cr * sp * cy + sr * cp * sy;
quat.z() = cr * cp * sy - sr * sp * cy;
quat.w() = cr * cp * cy + sr * sp * sy;
}
gem_state->quat[0] = quat.x();
gem_state->quat[1] = quat.y();
gem_state->quat[2] = quat.z();
gem_state->quat[3] = quat.w();
// Calculate handle position based on our world coordinate and the current orientation
constexpr ps_move_data::vect<3> offset_local_mm({0.f, 0.f, -45.f}); // handle is ~45 mm below sphere
const ps_move_data::vect<3> offset_world = ps_move_data::rotate_vector(quat, offset_local_mm);
gem_state->handle_pos[0] = gem_state->pos[0] - offset_world.x(); // Flip x offset
gem_state->handle_pos[1] = gem_state->pos[1] - offset_world.y(); // Flip y offset
gem_state->handle_pos[2] = gem_state->pos[2] + offset_world.z();
gem_state->handle_pos[3] = 0.f;
// Calculate velocity
if constexpr (!ps_move_data::use_imu_for_velocity)
{
move_data.update_velocity(shared_data.frame_timestamp_us, gem_state->pos);
@ -1920,6 +1943,10 @@ static inline void pos_to_gem_state(u32 gem_num, gem_config::gem_controller& con
{
gem_state->vel[i] = move_data.vel_world[i];
gem_state->accel[i] = move_data.accel_world[i];
// TODO: maybe this also needs to be adjusted depending on the orientation
gem_state->handle_vel[i] = gem_state->vel[i];
gem_state->handle_accel[i] = gem_state->accel[i];
}
}
@ -3612,7 +3639,7 @@ error_code cellGemReadExternalPortDeviceInfo(u32 gem_num, vm::ptr<u32> ext_id, v
if (!pad->move_data.external_device_read_requested)
{
*ext_id = controller.ext_id = pad->move_data.external_device_id;
std::memcpy(pad->move_data.external_device_read.data(), ext_info.get_ptr(), CELL_GEM_EXTERNAL_PORT_OUTPUT_SIZE);
std::memcpy(ext_info.get_ptr(), pad->move_data.external_device_read.data(), CELL_GEM_EXTERNAL_PORT_DEVICE_INFO_SIZE);
break;
}
}
@ -3876,13 +3903,15 @@ error_code cellGemUpdateStart(vm::cptr<void> camera_frame, u64 timestamp)
gem.camera_frame = camera_frame.addr();
if (!tracker.set_image(gem.camera_frame))
const bool image_set = tracker.set_image(gem.camera_frame);
tracker.wake_up_tracker();
if (!image_set)
{
return not_an_error(CELL_GEM_NO_VIDEO);
}
tracker.wake_up_tracker();
return CELL_OK;
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "Emu/Cell/ErrorCodes.h"
#include "Emu/Memory/vm_ptr.h"
// Error Codes

View File

@ -876,39 +876,42 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
// Sort the entries
{
const u32 order = setList->sortOrder;
const u32 type = setList->sortType;
std::sort(save_entries.begin(), save_entries.end(), [order, type](const SaveDataEntry& entry1, const SaveDataEntry& entry2) -> bool
auto comp = [type](const SaveDataEntry& entry1, const SaveDataEntry& entry2) -> bool
{
const bool mtime_lower = entry1.mtime < entry2.mtime;
const bool mtime_equal = entry1.mtime == entry2.mtime;
const bool subtitle_lower = entry1.subtitle < entry2.subtitle;
const bool subtitle_equal = entry1.subtitle == entry2.subtitle;
const bool revert_order = order == CELL_SAVEDATA_SORTORDER_DESCENT;
if (type == CELL_SAVEDATA_SORTTYPE_MODIFIEDTIME)
{
if (mtime_equal)
{
return subtitle_lower != revert_order;
return subtitle_lower;
}
return mtime_lower != revert_order;
return mtime_lower;
}
else if (type == CELL_SAVEDATA_SORTTYPE_SUBTITLE)
{
if (subtitle_equal)
{
return mtime_lower != revert_order;
return mtime_lower;
}
return subtitle_lower != revert_order;
return subtitle_lower;
}
ensure(false);
return true;
});
};
if (setList->sortOrder == CELL_SAVEDATA_SORTORDER_ASCENT)
std::sort(save_entries.begin(), save_entries.end(), comp);
else
std::sort(save_entries.rbegin(), save_entries.rend(), comp);
}
// Fill the listGet->dirList array

View File

@ -1462,7 +1462,12 @@ error_code cellVdecGetPicItem(ppu_thread& ppu, u32 handle, vm::pptr<CellVdecPicI
struct all_info_t
{
CellVdecPicItem picItem;
std::aligned_union_t<0, CellVdecAvcInfo, CellVdecDivxInfo, CellVdecMpeg2Info> picInfo;
union
{
CellVdecAvcInfo avcInfo;
CellVdecDivxInfo divxInfo;
CellVdecMpeg2Info mpeg2Info;
} picInfo;
};
AVFrame* frame{};

View File

@ -871,7 +871,7 @@ error_code sceNpDrmGetTimelimit(vm::cptr<char> path, vm::ptr<u64> time_remain)
}
// Convert time to milliseconds
s64 msec = *sec * 1000ll + *nsec / 1000ll;
s64 msec = *sec * 1000ll + *nsec / 1'000'000ll;
// Return the remaining time in microseconds
if (npd.activate_time != 0 && msec < npd.activate_time)
@ -1199,7 +1199,7 @@ error_code _sceNpBasicSendMessage(vm::cptr<SceNpId> to, vm::cptr<void> data, u32
.msgFeatures = {},
.data = std::vector<u8>(static_cast<const u8*>(data.get_ptr()), static_cast<const u8*>(data.get_ptr()) + size)};
std::set<std::string> npids;
npids.insert(std::string(to->handle.data));
npids.insert(np::npid_to_string(*to));
nph.send_message(msg_data, npids);
@ -1228,7 +1228,7 @@ error_code sceNpBasicSendMessageGui(ppu_thread& ppu, vm::cptr<SceNpBasicMessageD
sceNp.notice("sceNpBasicSendMessageGui: msgId: %d, mainType: %d, subType: %d, msgFeatures: %d, count: %d, npids: *0x%x", msg->msgId, msg->mainType, msg->subType, msg->msgFeatures, msg->count, msg->npids);
for (u32 i = 0; i < msg->count && msg->npids; i++)
{
sceNp.trace("sceNpBasicSendMessageGui: NpId[%d] = %s", i, static_cast<char*>(&msg->npids[i].handle.data[0]));
sceNp.trace("sceNpBasicSendMessageGui: NpId[%d] = %s", i, np::npid_to_string(msg->npids[i]));
}
sceNp.notice("sceNpBasicSendMessageGui: subject: %s", msg->subject);
sceNp.notice("sceNpBasicSendMessageGui: body: %s", msg->body);
@ -1398,7 +1398,7 @@ error_code sceNpBasicSendMessageGui(ppu_thread& ppu, vm::cptr<SceNpBasicMessageD
{
for (u32 i = 0; i < msg->count; i++)
{
npids.insert(std::string(msg->npids[i].handle.data));
npids.insert(np::npid_to_string(msg->npids[i]));
}
}
@ -4242,19 +4242,16 @@ error_code sceNpManagerGetTicket(vm::ptr<void> buffer, vm::ptr<u32> bufferSize)
}
const auto& ticket = nph.get_ticket();
*bufferSize = static_cast<u32>(ticket.size());
if (!buffer)
{
*bufferSize = static_cast<u32>(ticket.size());
return CELL_OK;
}
if (*bufferSize < ticket.size())
{
return SCE_NP_ERROR_INVALID_ARGUMENT;
}
memcpy(buffer.get_ptr(), ticket.data(), ticket.size());
const u32 size_read = std::min(::size32(ticket), static_cast<u32>(*bufferSize));
std::memcpy(buffer.get_ptr(), ticket.data(), size_read);
*bufferSize = size_read;
return CELL_OK;
}
@ -5676,7 +5673,7 @@ error_code scenp_score_record_score(s32 transId, SceNpScoreBoardId boardId, SceN
else
{
data = &gameInfo->nativeData[0];
data_size = 64;
data_size = sizeof(gameInfo->nativeData);
}
nph.record_score(trans_ctx, boardId, score, scoreComment, data, data_size, tmpRank, async);
@ -7144,7 +7141,7 @@ error_code sceNpUtilCanonicalizeNpIdForPsp(vm::ptr<SceNpId> npId)
error_code sceNpUtilCmpNpId(vm::ptr<SceNpId> id1, vm::ptr<SceNpId> id2)
{
sceNp.trace("sceNpUtilCmpNpId(id1=*0x%x(%s), id2=*0x%x(%s))", id1, id1 ? id1->handle.data : "", id2, id2 ? id2->handle.data : "");
sceNp.trace("sceNpUtilCmpNpId(id1=*0x%x(%s), id2=*0x%x(%s))", id1, id1 ? np::npid_to_string(*id1) : std::string(), id2, id2 ? np::npid_to_string(*id2) : std::string());
if (!id1 || !id2)
{

View File

@ -1397,9 +1397,9 @@ struct SceNpBasicMessageDetails
// Presence details of an user
struct SceNpBasicPresenceDetails
{
s8 title[SCE_NP_BASIC_PRESENCE_TITLE_SIZE_MAX];
s8 status[SCE_NP_BASIC_PRESENCE_STATUS_SIZE_MAX];
s8 comment[SCE_NP_BASIC_PRESENCE_COMMENT_SIZE_MAX];
char title[SCE_NP_BASIC_PRESENCE_TITLE_SIZE_MAX];
char status[SCE_NP_BASIC_PRESENCE_STATUS_SIZE_MAX];
char comment[SCE_NP_BASIC_PRESENCE_COMMENT_SIZE_MAX];
u8 data[SCE_NP_BASIC_MAX_PRESENCE_SIZE];
be_t<u32> size;
be_t<s32> state;
@ -1410,9 +1410,9 @@ struct SceNpBasicPresenceDetails2
{
be_t<u32> struct_size;
be_t<s32> state;
s8 title[SCE_NP_BASIC_PRESENCE_TITLE_SIZE_MAX];
s8 status[SCE_NP_BASIC_PRESENCE_EXTENDED_STATUS_SIZE_MAX];
s8 comment[SCE_NP_BASIC_PRESENCE_COMMENT_SIZE_MAX];
char title[SCE_NP_BASIC_PRESENCE_TITLE_SIZE_MAX];
char status[SCE_NP_BASIC_PRESENCE_EXTENDED_STATUS_SIZE_MAX];
char comment[SCE_NP_BASIC_PRESENCE_COMMENT_SIZE_MAX];
u8 data[SCE_NP_BASIC_MAX_PRESENCE_SIZE];
be_t<u32> size;
};
@ -1420,9 +1420,9 @@ struct SceNpBasicPresenceDetails2
// Country/region code
struct SceNpCountryCode
{
s8 data[2];
s8 term;
s8 padding[1];
char data[2];
char term;
char padding[1];
};
// Date information
@ -1451,8 +1451,8 @@ struct SceNpScoreGameInfo
// Ranking comment structure
struct SceNpScoreComment
{
s8 data[SCE_NP_SCORE_COMMENT_MAXLEN];
s8 term[1];
char data[SCE_NP_SCORE_COMMENT_MAXLEN];
char term[1];
};
// Ranking information structure
@ -1524,15 +1524,15 @@ struct SceNpScoreNpIdPcId
// Basic clan information to be used in raking
struct SceNpScoreClanBasicInfo
{
s8 clanName[SCE_NP_CLANS_CLAN_NAME_MAX_LENGTH + 1];
s8 clanTag[SCE_NP_CLANS_CLAN_TAG_MAX_LENGTH + 1];
char clanName[SCE_NP_CLANS_CLAN_NAME_MAX_LENGTH + 1];
char clanTag[SCE_NP_CLANS_CLAN_TAG_MAX_LENGTH + 1];
u8 reserved[10];
};
// Clan member information handled in ranking
struct SceNpScoreClansMemberDescription
{
s8 description[SCE_NP_CLANS_CLAN_DESCRIPTION_MAX_LENGTH + 1];
char description[SCE_NP_CLANS_CLAN_DESCRIPTION_MAX_LENGTH + 1];
};
// Clan ranking information

View File

@ -1135,7 +1135,7 @@ error_code sceNpMatching2ContextStartAsync(SceNpMatching2ContextId ctxId, u32 ti
{
sysutil_register_cb([=, context_callback = ctx->context_callback, context_callback_param = ctx->context_callback_param](ppu_thread& cb_ppu) -> s32
{
context_callback(cb_ppu, ctxId, SCE_NP_MATCHING2_CONTEXT_EVENT_Start, SCE_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ACTION, 0, ctx->context_callback_param);
context_callback(cb_ppu, ctxId, SCE_NP_MATCHING2_CONTEXT_EVENT_Start, SCE_NP_MATCHING2_EVENT_CAUSE_CONTEXT_ACTION, 0, context_callback_param);
return 0;
});
}
@ -1760,7 +1760,7 @@ error_code sceNpMatching2ContextStop(SceNpMatching2ContextId ctxId)
const auto ctx = get_match2_context(ctxId);
if (!ctx)
return SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND;
if (!ctx->started.compare_and_swap_test(1, 0))
return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_STARTED;

View File

@ -1026,14 +1026,14 @@ error_code sceNpTrophyUnlockTrophy(ppu_thread& ppu, u32 context, u32 handle, s32
auto& trophy_manager = g_fxo->get<sce_np_trophy_manager>();
reader_lock lock(trophy_manager.mtx);
std::scoped_lock lock(trophy_manager.mtx);
if (!trophy_manager.is_initialized)
{
return SCE_NP_TROPHY_ERROR_NOT_INITIALIZED;
}
const auto [ctxt, error] = trophy_manager.get_context_ex(context, handle);
const auto [ctxt, error] = trophy_manager.get_context_ex(context, handle, true);
if (error)
{
@ -1184,9 +1184,9 @@ error_code sceNpTrophyGetTrophyUnlockState(u32 context, u32 handle, vm::ptr<SceN
for (u32 id = 0; id < count_; id++)
{
if (tropusr->GetTrophyUnlockState(id))
flags->flag_bits[id / 32] |= 1 << (id % 32);
flags->flag_bits[id / 32] |= 1u << (id % 32);
else
flags->flag_bits[id / 32] &= ~(1 << (id % 32));
flags->flag_bits[id / 32] &= ~(1u << (id % 32));
}
return CELL_OK;

View File

@ -2535,7 +2535,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
// SLDI mnemonic
reg_state_t rs = get_reg(op.rs);
if (!rs.shift_left(op.sh32, reg_tag_allocator))
if (!rs.shift_left(sh, reg_tag_allocator))
{
unmap_reg(op.ra);
}

View File

@ -330,7 +330,7 @@ void comment_constant(std::string& last_opcode, u64 value, bool print_float = fa
// Comment constant formation
fmt::append(last_opcode, " #0x%xh", value);
if (print_float && ((value >> 31) <= 1u || (value >> 31) == 0x1'ffff'ffffu))
if (print_float && ((value >> 31) <= 1u || (value >> 31) == 0x1'ffff'ffffu) && (value > 0x3fffff && (value << 32 >> 32) < 0xffc00000))
{
const f32 float_val = std::bit_cast<f32>(static_cast<u32>(value));

View File

@ -1004,7 +1004,7 @@ static import_result_t ppu_load_imports(const ppu_module<lv2_obj>& _module, std:
// Check address
// TODO: The address of use should be extracted from analyser instead
if (fstub && fstub >= _module.segs[0].addr && fstub <= _module.segs[0].addr + _module.segs[0].size)
if (fstub && fstub >= _module.segs[0].addr && fstub < _module.segs[0].addr + _module.segs[0].size)
{
nid_to_use_addr.emplace(fnid, fstub);
}
@ -1895,7 +1895,7 @@ shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, bool virtual_load, c
}
else
{
ppu_loader.error("Library %s: PRX library info not found");
ppu_loader.error("Library: PRX library info not found");
}
prx->start.set(prx->specials[0xbc9a0086]);
@ -3192,7 +3192,7 @@ bool ppu_load_rel_exec(const ppu_rel_object& elf)
for (const auto& s : elf.shdrs)
{
if (s.sh_type != sec_type::sht_progbits)
if (s.sh_type == sec_type::sht_progbits)
{
memsize = utils::align<u32>(memsize + vm::cast(s.sh_size), 128);
}

View File

@ -3867,12 +3867,12 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
if (mself.read(hdr) && hdr.get_count(mself.size()))
{
std::set<u64> offs;
for (u32 j = 0; j < hdr.count; j++)
{
mself_record rec{};
std::set<u64> offs;
if (mself.read(rec) && rec.get_pos(mself.size()))
{
if (rec.size <= 0x20)

View File

@ -550,11 +550,12 @@ void PPUTranslator::CallFunction(u64 target, Value* indirect)
else if (_target >= caddr && _target <= cend)
{
u32 target_last = static_cast<u32>(_target);
std::unordered_set<u32> passed_targets{target_last};
// Try to follow unconditional branches as long as there is no infinite loop
while (target_last != _target)
// !! Triggers compilation issues in Asura's Wrath in other parts of the code
// !! See https://github.com/RPCS3/rpcs3/issues/18287
while (false)
{
const ppu_opcode_t op{*ensure(m_info.get_ptr<u32>(target_last))};
const ppu_itype::type itype = g_ppu_itype.decode(op.opcode);
@ -1304,7 +1305,7 @@ void PPUTranslator::VMADDFP(ppu_opcode_t op)
if (!m_use_fma && data == v128{})
{
set_vr(op.vd, vec_handle_result(a * c + fsplat<f32[4]>(0.f)));
ppu_log.notice("LLVM: VMADDFP with -0 addend at [0x%08x]", m_addr + (m_reloc ? m_reloc->addr : 0));
ppu_log.notice("LLVM: VMADDFP with +0 addend at [0x%08x]", m_addr + (m_reloc ? m_reloc->addr : 0));
return;
}
}
@ -3680,9 +3681,7 @@ void PPUTranslator::STVLX(ppu_opcode_t op)
const auto addr = op.ra ? m_ir->CreateAdd(GetGpr(op.ra), GetGpr(op.rb)) : GetGpr(op.rb);
const auto data = pshufb(get_vr<u8[16]>(op.vs), build<u8[16]>(127, 126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112) + vsplat<u8[16]>(trunc<u8>(value<u64>(addr) & 0xf)));
const auto mask = bitcast<bool[16]>(splat<u16>(0xffff) << trunc<u16>(value<u64>(addr) & 0xf));
const auto ptr = value<u8(*)[16]>(GetMemory(m_ir->CreateAnd(addr, ~0xfull)));
const auto align = splat<u32>(16);
eval(llvm_calli<void, decltype(data), decltype(ptr), decltype(align), decltype(mask)>{"llvm.masked.store.v16i8.p0", {data, ptr, align, mask}});
m_ir->CreateMaskedStore(data.eval(m_ir), GetMemory(m_ir->CreateAnd(addr, ~0xfull)), llvm::Align(16), mask.eval(m_ir));
}
void PPUTranslator::STDBRX(ppu_opcode_t op)
@ -3710,9 +3709,7 @@ void PPUTranslator::STVRX(ppu_opcode_t op)
const auto addr = op.ra ? m_ir->CreateAdd(GetGpr(op.ra), GetGpr(op.rb)) : GetGpr(op.rb);
const auto data = pshufb(get_vr<u8[16]>(op.vs), build<u8[16]>(255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240) + vsplat<u8[16]>(trunc<u8>(value<u64>(addr) & 0xf)));
const auto mask = bitcast<bool[16]>(trunc<u16>(splat<u64>(0xffff) << (value<u64>(addr) & 0xf) >> 16));
const auto ptr = value<u8(*)[16]>(GetMemory(m_ir->CreateAnd(addr, ~0xfull)));
const auto align = splat<u32>(16);
eval(llvm_calli<void, decltype(data), decltype(ptr), decltype(align), decltype(mask)>{"llvm.masked.store.v16i8.p0", {data, ptr, align, mask}});
m_ir->CreateMaskedStore(data.eval(m_ir), GetMemory(m_ir->CreateAnd(addr, ~0xfull)), llvm::Align(16), mask.eval(m_ir));
}
void PPUTranslator::STFSUX(ppu_opcode_t op)

View File

@ -51,22 +51,22 @@ struct spu_itype
RDCH,
RCHCNT,
BR, // branch_tag first
BR, // branch_tag first, zregmod_tag (2) first
BRA,
BRNZ,
BRZ,
BRHNZ,
BRHZ,
BRSL,
BRASL,
IRET,
BI,
BISLED,
BISL,
BIZ,
BINZ,
BIHZ,
BIHNZ, // branch_tag last
BIHNZ, // zregmod_tag (2) last
BRSL,
BRASL,
BISL, // branch_tag last
ILH, // constant_tag_first
ILHU,
@ -245,7 +245,7 @@ struct spu_itype
// Test for branch instruction
friend constexpr bool operator &(type value, branch_tag)
{
return value >= BR && value <= BIHNZ;
return value >= BR && value <= BISL;
}
// Test for floating point instruction
@ -299,7 +299,7 @@ struct spu_itype
// Test for non register-modifying instruction
friend constexpr bool operator &(type value, zregmod_tag)
{
return value >= HEQ && value <= STQR;
return (value >= HEQ && value <= STQR) || (value >= BR && value <= BIHNZ);
}
};

View File

@ -1176,108 +1176,6 @@ void spu_cache::initialize(bool build_existing_cache)
if ((g_cfg.core.spu_decoder == spu_decoder_type::asmjit || g_cfg.core.spu_decoder == spu_decoder_type::llvm) && !func_list.empty())
{
spu_log.success("SPU Runtime: Built %u functions.", func_list.size());
if (g_cfg.core.spu_debug)
{
std::string dump;
dump.reserve(10'000'000);
std::map<std::span<u8>, spu_program*, span_less<u8>> sorted;
for (auto&& f : func_list)
{
// Interpret as a byte string
std::span<u8> data = {reinterpret_cast<u8*>(f.data.data()), f.data.size() * sizeof(u32)};
sorted[data] = &f;
}
std::unordered_set<u32> depth_n;
u32 n_max = 0;
for (auto&& [bytes, f] : sorted)
{
{
sha1_context ctx;
u8 output[20];
sha1_starts(&ctx);
sha1_update(&ctx, bytes.data(), bytes.size());
sha1_finish(&ctx, output);
fmt::append(dump, "\n\t[%s] ", fmt::base57(output));
}
u32 depth_m = 0;
for (auto&& [data, f2] : sorted)
{
u32 depth = 0;
if (f2 == f)
{
continue;
}
for (u32 i = 0; i < bytes.size(); i++)
{
if (i < data.size() && data[i] == bytes[i])
{
depth++;
}
else
{
break;
}
}
depth_n.emplace(depth);
depth_m = std::max(depth, depth_m);
}
fmt::append(dump, "c=%06d,d=%06d ", depth_n.size(), depth_m);
bool sk = false;
for (u32 i = 0; i < std::min<usz>(bytes.size(), std::max<usz>(256, depth_m)); i++)
{
if (depth_m == i)
{
dump += '|';
sk = true;
}
fmt::append(dump, "%02x", bytes[i]);
if (i % 4 == 3)
{
if (sk)
{
sk = false;
}
else
{
dump += ' ';
}
dump += ' ';
}
}
fmt::append(dump, "\n\t%49s", "");
for (u32 i = 0; i < std::min<usz>(f->data.size(), std::max<usz>(64, utils::aligned_div<u32>(depth_m, 4))); i++)
{
fmt::append(dump, "%-10s", g_spu_iname.decode(std::bit_cast<be_t<u32>>(f->data[i])));
}
n_max = std::max(n_max, ::size32(depth_n));
depth_n.clear();
}
spu_log.notice("SPU Cache Dump (max_c=%d): %s", n_max, dump);
}
}
// Initialize global cache instance
@ -3705,6 +3603,11 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
default:
{
if (type & spu_itype::zregmod)
{
break;
}
// Unconst
const u32 op_rt = type & spu_itype::_quadrop ? +op.rt4 : +op.rt;
m_regmod[pos / 4] = op_rt;
@ -7488,6 +7391,7 @@ void spu_recompiler_base::dump(const spu_program& result, std::string& out)
SPUDisAsm dis_asm(cpu_disasm_mode::dump, reinterpret_cast<const u8*>(result.data.data()), result.lower_bound);
std::string hash;
be_t<u64> hash_start{};
if (!result.data.empty())
{
@ -7498,6 +7402,7 @@ void spu_recompiler_base::dump(const spu_program& result, std::string& out)
sha1_update(&ctx, reinterpret_cast<const u8*>(result.data.data()), result.data.size() * 4);
sha1_finish(&ctx, output);
fmt::append(hash, "%s", fmt::base57(output));
std::memcpy(&hash_start, output, sizeof(hash_start));
}
else
{
@ -7510,7 +7415,7 @@ void spu_recompiler_base::dump(const spu_program& result, std::string& out)
{
if (m_block_info[bb.first / 4])
{
fmt::append(out, "A: [0x%05x] %s\n", bb.first, m_entry_info[bb.first / 4] ? (m_ret_info[bb.first / 4] ? "Chunk" : "Entry") : "Block");
fmt::append(out, "A: [0x%05x] %s [%s]\n", bb.first, m_entry_info[bb.first / 4] ? (m_ret_info[bb.first / 4] ? "Chunk" : "Entry") : "Block", spu_block_hash{(hash_start & -65536) + bb.first / 4});
fmt::append(out, "\t F: 0x%05x\n", bb.second.func);

View File

@ -903,8 +903,14 @@ public:
if (auto [is_const, value] = try_get_const_equal_value_array<u32>(+op.ra); is_const)
{
if (value % 0x200 != 0)
{
// si10 is overwritten - likely an analysis mistake
return;
}
// Comment constant formation
comment_constant(last_opcode, value | static_cast<u32>(op.si10));
comment_constant(last_opcode, value | static_cast<u32>(op.si10), false);
}
}
void ORHI(spu_opcode_t op)
@ -941,8 +947,14 @@ public:
if (auto [is_const, value] = try_get_const_equal_value_array<u32>(op.ra); is_const)
{
if (value % 0x200 != 0)
{
// si10 is overwritten - likely an analysis mistake
return;
}
// Comment constant formation
comment_constant(last_opcode, value + static_cast<u32>(op.si10));
comment_constant(last_opcode, value + static_cast<u32>(op.si10), false);
}
}
void AHI(spu_opcode_t op)
@ -963,8 +975,14 @@ public:
if (auto [is_const, value] = try_get_const_equal_value_array<u32>(op.ra); is_const)
{
if (value % 0x200 != 0)
{
// si10 is overwritten - likely an analysis mistake
return;
}
// Comment constant formation
comment_constant(last_opcode, value ^ static_cast<u32>(op.si10));
comment_constant(last_opcode, value ^ static_cast<u32>(op.si10), false);
}
}
void XORHI(spu_opcode_t op)

View File

@ -2160,6 +2160,14 @@ public:
}
}
if (bb.preds.size() >= 2)
{
if (g_cfg.core.spu_prof || g_cfg.core.spu_debug)
{
m_ir->CreateStore(m_ir->getInt64((m_hash_start & -65536) | (baddr >> 2)), spu_ptr(&spu_thread::block_hash));
}
}
// State check at the beginning of the chunk
if (need_check || (bi == 0 && g_cfg.core.spu_block_size != spu_block_size_type::safe))
{
@ -2802,12 +2810,9 @@ public:
std::string& llvm_log = function_log;
raw_string_ostream out(llvm_log);
if (g_cfg.core.spu_debug)
{
fmt::append(llvm_log, "LLVM IR at 0x%x:\n", func.entry_point);
out << *_module; // print IR
out << "\n\n";
}
fmt::append(llvm_log, "LLVM IR at 0x%x:\n", func.entry_point);
out << *_module; // print IR
out << "\n\n";
if (verifyModule(*_module, &out))
{
@ -3274,12 +3279,9 @@ public:
std::string llvm_log;
raw_string_ostream out(llvm_log);
if (g_cfg.core.spu_debug)
{
fmt::append(llvm_log, "LLVM IR (interpreter):\n");
out << *_module; // print IR
out << "\n\n";
}
fmt::append(llvm_log, "LLVM IR (interpreter):\n");
out << *_module; // print IR
out << "\n\n";
if (verifyModule(*_module, &out))
{
@ -4757,6 +4759,50 @@ public:
}
const auto a = get_vr<s32[4]>(op.ra);
#ifdef ARCH_ARM64
// Use dot product instructions with special values to shift then sum results into the preferred slot
if (m_use_dotprod)
{
if (match_vr<s32[4], s64[2]>(op.ra, [&](auto c, auto MP)
{
using VT = typename decltype(MP)::type;
if (auto [ok, x] = match_expr(c, sext<VT>(match<bool[std::extent_v<VT>]>())); ok)
{
const auto zeroes = splat<u32[4]>(0);
const auto es = zshuffle(bitcast<u8[16]>(a), 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 0, 4, 8, 12);
set_vr(op.rt, sdot(zeroes, es, build<u8[16]>(
-0x01, -0x02, -0x04, -0x08,
-0x01, -0x02, -0x04, -0x08,
-0x01, -0x02, -0x04, -0x08,
-0x01, -0x02, -0x04, -0x08
)));
return true;
}
return false;
}))
{
return;
}
const auto zeroes = splat<u32[4]>(0);
const auto masked = a & 0x01;
const auto es = zshuffle(bitcast<u8[16]>(masked), 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 0, 4, 8, 12);
set_vr(op.rt, udot(zeroes, es, build<u8[16]>(
0x01, 0x02, 0x04, 0x08,
0x01, 0x02, 0x04, 0x08,
0x01, 0x02, 0x04, 0x08,
0x01, 0x02, 0x04, 0x08
)));
return;
}
#endif
const auto m = zext<u32>(bitcast<i4>(trunc<bool[4]>(a)));
set_vr(op.rt, insert(splat<u32[4]>(0), 3, eval(m)));
}
@ -4772,6 +4818,54 @@ public:
}
const auto a = get_vr<s16[8]>(op.ra);
#ifdef ARCH_ARM64
// Use dot product instructions with special values to shift then sum results into the preferred slot
if (m_use_dotprod)
{
if (match_vr<s16[8], s32[4], s64[2]>(op.ra, [&](auto c, auto MP)
{
using VT = typename decltype(MP)::type;
if (auto [ok, x] = match_expr(c, sext<VT>(match<bool[std::extent_v<VT>]>())); ok)
{
const auto zeroes = splat<u32[4]>(0);
const auto es = zshuffle(bitcast<u8[16]>(a), 16, 16, 16, 16, 16, 16, 16, 16, 0, 2, 4, 6, 8, 10, 12, 14);
const auto extracted = sdot(zeroes, es, build<u8[16]>(
-0x01, -0x02, -0x04, -0x08,
-0x10, -0x20, -0x40, -0x80,
-0x01, -0x02, -0x04, -0x08,
-0x10, -0x20, -0x40, -0x80
));
set_vr(op.rt, addp(zeroes, bitcast<u32[4]>(extracted)));
return true;
}
return false;
}))
{
return;
}
const auto zeroes = splat<u32[4]>(0);
const auto masked = a & 0x01;
const auto es = zshuffle(bitcast<u8[16]>(masked), 16, 16, 16, 16, 16, 16, 16, 16, 0, 2, 4, 6, 8, 10, 12, 14);
const auto extracted = udot(zeroes, es, build<u8[16]>(
0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80
));
set_vr(op.rt, addp(zeroes, bitcast<u32[4]>(extracted)));
return;
}
#endif
const auto m = zext<u32>(bitcast<u8>(trunc<bool[8]>(a)));
set_vr(op.rt, insert(splat<u32[4]>(0), 3, eval(m)));
}
@ -4780,6 +4874,53 @@ public:
{
const auto a = get_vr<u8[16]>(op.ra);
#ifdef ARCH_ARM64
// Use dot product instructions with special values to shift then sum results into the preferred slot
if (m_use_dotprod)
{
if (match_vr<s8[16], s16[8], s32[4], s64[2]>(op.ra, [&](auto c, auto MP)
{
using VT = typename decltype(MP)::type;
if (auto [ok, x] = match_expr(c, sext<VT>(match<bool[std::extent_v<VT>]>())); ok)
{
const auto zeroes = splat<u32[4]>(0);
const auto extracted = sdot(zeroes, a, build<u8[16]>(
-0x01, -0x02, -0x04, -0x08,
-0x10, -0x20, -0x40, -0x80,
-0x01, -0x02, -0x04, -0x08,
-0x10, -0x20, -0x40, -0x80
));
const auto es = zshuffle(bitcast<u8[16]>(extracted), 16, 16, 16, 16, 16, 16, 16, 16, 0, 8, 4, 12, 16, 16, 16, 16);
const auto zeroes16 = splat<u16[8]>(0);
set_vr(op.rt, addp(zeroes16, bitcast<u16[8]>(es)));
return true;
}
return false;
}))
{
return;
}
const auto zeroes = splat<u32[4]>(0);
const auto masked = a & 0x01;
const auto extracted = udot(zeroes, masked, build<u8[16]>(
0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80,
0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80
));
const auto es = zshuffle(bitcast<u8[16]>(extracted), 16, 16, 16, 16, 16, 16, 16, 16, 0, 8, 4, 12, 16, 16, 16, 16);
const auto zeroes16 = splat<u16[8]>(0);
set_vr(op.rt, addp(zeroes16, bitcast<u16[8]>(es)));
return;
}
#endif
if (m_use_gfni)
{
const auto as = zshuffle(a, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8);
@ -5311,13 +5452,24 @@ public:
return;
}
#ifdef ARCH_ARM64
if (m_use_dotprod)
#else
if (m_use_vnni)
#endif
{
const auto [a, b] = get_vrs<u32[4]>(op.ra, op.rb);
const auto zeroes = splat<u32[4]>(0);
#ifdef ARCH_ARM64
const auto [a, b] = get_vrs<u8[16]>(op.ra, op.rb);
const auto ones = splat<u8[16]>(0x01);
const auto ax = bitcast<u16[8]>(udot(zeroes, a, ones));
const auto bx = bitcast<u16[8]>(udot(zeroes, b, ones));
#else
const auto [a, b] = get_vrs<u32[4]>(op.ra, op.rb);
const auto ones = splat<u32[4]>(0x01010101);
const auto ax = bitcast<u16[8]>(vpdpbusd(zeroes, a, ones));
const auto bx = bitcast<u16[8]>(vpdpbusd(zeroes, b, ones));
#endif
set_vr(op.rt, shuffle2(ax, bx, 0, 8, 2, 10, 4, 12, 6, 14));
return;
}

View File

@ -164,7 +164,7 @@ void fmt_class_string<spu_block_hash>::format(std::string& out, u64 arg)
out.resize(out.size() - 4);
// Print chunk address from lowest 16 bits
fmt::append(out, "...chunk-0x%05x", (arg & 0xffff) * 4);
fmt::append(out, "-0x%05x", (arg & 0xffff) * 4);
}
enum class spu_block_hash_short : u64{};

View File

@ -2219,6 +2219,28 @@ void lv2_obj::prepare_for_sleep(cpu_thread& cpu)
cpu_counter::remove(&cpu);
}
ppu_thread* lv2_obj::get_running_ppu(u32 index)
{
usz thread_count = g_cfg.core.ppu_threads;
if (index >= thread_count)
{
return nullptr;
}
auto target = atomic_storage<ppu_thread*>::load(g_ppu);
for (usz cur = 0; target; target = atomic_storage<ppu_thread*>::load(target->next_ppu), cur++)
{
if (cur == index)
{
return target;
}
}
return nullptr;
}
void lv2_obj::notify_all() noexcept
{
for (auto cpu : g_to_notify)

View File

@ -59,6 +59,7 @@ CellError lv2_cond::on_id_create()
if (!mutex)
{
_mutex = static_cast<shared_ptr<lv2_obj>>(ensure(idm::get_unlocked<lv2_obj, lv2_mutex>(mtx_id)));
mutex = static_cast<lv2_mutex*>(_mutex.get());
}
// Defer function

View File

@ -174,20 +174,28 @@ bool lv2_config_service_listener::check_service(const lv2_config_service& servic
return true;
}
bool lv2_config_service_listener::notify(const shared_ptr<lv2_config_service_event>& event)
{
service_events.emplace_back(event);
return event->notify();
}
bool lv2_config_service_listener::notify(const shared_ptr<lv2_config_service>& service)
{
if (!check_service(*service))
return false;
{
std::lock_guard lock(mutex_service_events);
// Create service event and notify queue!
const auto event = lv2_config_service_event::create(handle, service, *this);
return notify(event);
if (!check_service(*service))
return false;
// Create service event and notify queue!
const auto event = lv2_config_service_event::create(handle, service, *this);
service_events.emplace_back(event);
if (!event->notify())
{
// If we fail to deliver the event to the queue just clean the event up or it'll hold the listener alive forever
g_fxo->get<lv2_config>().remove_service_event(event->id);
service_events.pop_back();
return false;
}
}
return true;
}
void lv2_config_service_listener::notify_all()
@ -267,7 +275,7 @@ void lv2_config_service_event::write(sys_config_service_event_t *dst) const
{
const auto registered = service->is_registered();
dst->service_listener_handle = listener.get_id();
dst->service_listener_handle = listener_id;
dst->registered = registered;
dst->service_id = service->id;
dst->user_id = service->user_id;
@ -346,7 +354,7 @@ error_code sys_config_get_service_event(u32 config_hdl, u32 event_id, vm::ptr<sy
// Find service_event object
const auto event = g_fxo->get<lv2_config>().find_event(event_id);
if (!event)
if (!event || event->handle != cfg)
{
return CELL_ESRCH;
}

View File

@ -296,11 +296,10 @@ private:
// The service listener owns the service events - service events will not be freed as long as their corresponding listener exists
// This has been confirmed to be the case in realhw
shared_mutex mutex_service_events;
std::vector<shared_ptr<lv2_config_service_event>> service_events;
shared_ptr<lv2_config_handle> handle;
bool notify(const shared_ptr<lv2_config_service_event>& event);
public:
const sys_config_service_id service_id;
const u64 min_verbosity;
@ -370,14 +369,14 @@ public:
// This has been confirmed to be the case in realhw
const shared_ptr<lv2_config_handle> handle;
const shared_ptr<lv2_config_service> service;
const lv2_config_service_listener& listener;
const u32 listener_id;
// Constructors (should not be used directly)
lv2_config_service_event(shared_ptr<lv2_config_handle> _handle, shared_ptr<lv2_config_service> _service, const lv2_config_service_listener& _listener) noexcept
: id(get_next_id())
, handle(std::move(_handle))
, service(std::move(_service))
, listener(_listener)
, listener_id(_listener.get_id())
{
}

View File

@ -105,7 +105,7 @@ error_code sys_dbg_write_process_memory(s32 pid, u32 address, u32 size, vm::cptr
i += op_size;
}
if (!is_exec || i >= end)
if ((!is_exec || i >= end) && exec_update_size > 0)
{
// Commit executable data update
// The read memory is also super ptr so memmove can work correctly on all implementations

View File

@ -170,8 +170,7 @@ CellError lv2_event_queue::send(lv2_event event, bool* notified_thread, lv2_even
{
if (auto cpu = get_current_cpu_thread())
{
cpu->state += cpu_flag::again;
cpu->state += cpu_flag::exit;
cpu->state += cpu_flag::again + cpu_flag::exit;
}
sys_event.warning("Ignored event!");
@ -309,6 +308,15 @@ error_code sys_event_queue_destroy(ppu_thread& ppu, u32 equeue_id, s32 mode)
return CELL_EBUSY;
}
for (auto cpu = head; cpu; cpu = cpu->get_next_cpu())
{
if (cpu->state & cpu_flag::again)
{
ppu.state += cpu_flag::again;
return CELL_EAGAIN;
}
}
if (!queue.events.empty())
{
// Copy events for logging, does not empty
@ -321,17 +329,6 @@ error_code sys_event_queue_destroy(ppu_thread& ppu, u32 equeue_id, s32 mode)
{
qlock.unlock();
}
else
{
for (auto cpu = head; cpu; cpu = cpu->get_next_cpu())
{
if (cpu->state & cpu_flag::again)
{
ppu.state += cpu_flag::again;
return CELL_EAGAIN;
}
}
}
return {};
});
@ -621,7 +618,7 @@ error_code sys_event_port_create(cpu_thread& cpu, vm::ptr<u32> eport_id, s32 por
sys_event.warning("sys_event_port_create(eport_id=*0x%x, port_type=%d, name=0x%llx)", eport_id, port_type, name);
if (port_type != SYS_EVENT_PORT_LOCAL && port_type != 3)
if (port_type != SYS_EVENT_PORT_LOCAL && port_type != SYS_EVENT_PORT_IPC)
{
sys_event.error("sys_event_port_create(): unknown port type (%d)", port_type);
return CELL_EINVAL;
@ -675,8 +672,9 @@ error_code sys_event_port_connect_local(cpu_thread& cpu, u32 eport_id, u32 equeu
std::lock_guard lock(id_manager::g_mutex);
const auto port = idm::check_unlocked<lv2_obj, lv2_event_port>(eport_id);
auto queue = idm::get_unlocked<lv2_obj, lv2_event_queue>(equeue_id);
if (!port || !idm::check_unlocked<lv2_obj, lv2_event_queue>(equeue_id))
if (!port || !queue)
{
return CELL_ESRCH;
}
@ -691,7 +689,7 @@ error_code sys_event_port_connect_local(cpu_thread& cpu, u32 eport_id, u32 equeu
return CELL_EISCONN;
}
port->queue = idm::get_unlocked<lv2_obj, lv2_event_queue>(equeue_id);
port->queue = std::move(queue);
return CELL_OK;
}

View File

@ -7,7 +7,6 @@
#include <deque>
class cpu_thread;
class spu_thrread;
// Event Queue Type
enum : u32

View File

@ -19,6 +19,22 @@ lv2_event_flag::lv2_event_flag(utils::serial& ar)
ar(pattern);
}
// Always set result
struct sys_event_store_result
{
vm::ptr<u64> ptr;
u64 val = 0;
~sys_event_store_result() noexcept
{
if (ptr)
{
cpu_thread::get_current()->check_state();
*ptr = val;
}
}
};
std::function<void(void*)> lv2_event_flag::load(utils::serial& ar)
{
return load_func(make_shared<lv2_event_flag>(stx::exact_t<utils::serial&>(ar)));
@ -120,21 +136,7 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm
ppu.gpr[5] = mode;
ppu.gpr[6] = 0;
// Always set result
struct store_result
{
vm::ptr<u64> ptr;
u64 val = 0;
~store_result() noexcept
{
if (ptr)
{
cpu_thread::get_current()->check_state();
*ptr = val;
}
}
} store{result};
sys_event_store_result store{result};
if (!lv2_event_flag::check_mode(mode))
{
@ -273,21 +275,7 @@ error_code sys_event_flag_trywait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode,
sys_event_flag.trace("sys_event_flag_trywait(id=0x%x, bitptn=0x%llx, mode=0x%x, result=*0x%x)", id, bitptn, mode, result);
// Always set result
struct store_result
{
vm::ptr<u64> ptr;
u64 val = 0;
~store_result() noexcept
{
if (ptr)
{
cpu_thread::get_current()->check_state();
*ptr = val;
}
}
} store{result};
sys_event_store_result store{result};
if (!lv2_event_flag::check_mode(mode))
{
@ -556,8 +544,6 @@ error_code sys_event_flag_get(ppu_thread& ppu, u32 id, vm::ptr<u64> flags)
return +flag.pattern;
});
ppu.check_state();
if (!flag)
{
if (flags) *flags = 0;
@ -569,6 +555,8 @@ error_code sys_event_flag_get(ppu_thread& ppu, u32 id, vm::ptr<u64> flags)
return CELL_EFAULT;
}
ppu.check_state();
*flags = flag.ret;
return CELL_OK;
}

View File

@ -382,10 +382,12 @@ lv2_fs_object::lv2_fs_object(utils::serial& ar, bool)
u64 lv2_file::op_read(const fs::file& file, vm::ptr<void> buf, u64 size, u64 opt_pos)
{
if (u64 region = buf.addr() >> 28, region_end = (buf.addr() & 0xfff'ffff) + (size & 0xfff'ffff); region == region_end && ((region >> 28) == 0 || region >= 0xC))
if (u64 region = buf.addr() >> 28, region_end = (buf.addr() + size) >> 28;
size < u32{umax} && region == region_end && (region == 0 || region == 0xD) && vm::check_addr(buf.addr(), vm::page_writable, static_cast<u32>(size)))
{
// Optimize reads from safe memory
return (opt_pos == umax ? file.read(buf.get_ptr(), size) : file.read_at(opt_pos, buf.get_ptr(), size));
const auto buf_ptr = vm::get_super_ptr(buf.addr());
return (opt_pos == umax ? file.read(buf_ptr, size) : file.read_at(opt_pos, buf_ptr, size));
}
// Copy data from intermediate buffer (avoid passing vm pointer to a native API)
@ -412,6 +414,14 @@ u64 lv2_file::op_read(const fs::file& file, vm::ptr<void> buf, u64 size, u64 opt
u64 lv2_file::op_write(const fs::file& file, vm::cptr<void> buf, u64 size)
{
if (u64 region = buf.addr() >> 28, region_end = (buf.addr() + size) >> 28;
size < u32{umax} && region == region_end && (region == 0 || region == 0xD) && vm::check_addr(buf.addr(), vm::page_readable, static_cast<u32>(size)))
{
// Optimize writes from safe memory
const auto buf_ptr = vm::get_super_ptr(buf.addr());
return file.write(buf_ptr, size);
}
// Copy data to intermediate buffer (avoid passing vm pointer to a native API)
std::vector<uchar> local_buf(std::min<u64>(size, 65536));
@ -890,7 +900,7 @@ lv2_file::open_raw_result_t lv2_file::open_raw(const std::string& local_path, s3
switch (auto error = fs::g_tls_error)
{
case fs::error::noent: return {CELL_ENOENT};
default: sys_fs.error("lv2_file::open(): unknown error %s", error);
default: sys_fs.error("lv2_file::open(): unknown error %s", error); break;
}
return {CELL_EIO};
@ -1391,7 +1401,8 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u32> fd)
// Add additional entries for split file candidates (while ends with .66600)
while (mp.mp != &g_mp_sys_dev_hdd1 && data.back().name.ends_with(".66600"))
{
data.emplace_back(data.back()).name.resize(data.back().name.size() - 6);
fs::dir_entry copy = data.back();
data.emplace_back(copy).name.resize(copy.name.size() - 6);
}
}
@ -2147,6 +2158,7 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
sys_fs.notice("sys_fs_fcntl(0xc0000006): %s", vpath);
// Check only mountpoint
vpath = vpath.substr(0, vpath.find_first_of('\0'));
vpath = vpath.substr(0, vpath.find_first_of("/", 1));
// Some mountpoints seem to be handled specially
@ -2635,8 +2647,6 @@ error_code sys_fs_lseek(ppu_thread& ppu, u32 fd, s64 offset, s32 whence, vm::ptr
error_code sys_fs_fdatasync(ppu_thread& ppu, u32 fd)
{
lv2_obj::sleep(ppu);
sys_fs.trace("sys_fs_fdadasync(fd=%d)", fd);
const auto file = idm::get_unlocked<lv2_fs_object, lv2_file>(fd);
@ -2661,8 +2671,6 @@ error_code sys_fs_fdatasync(ppu_thread& ppu, u32 fd)
error_code sys_fs_fsync(ppu_thread& ppu, u32 fd)
{
lv2_obj::sleep(ppu);
sys_fs.trace("sys_fs_fsync(fd=%d)", fd);
const auto file = idm::get_unlocked<lv2_fs_object, lv2_file>(fd);
@ -2903,14 +2911,6 @@ error_code sys_fs_chmod(ppu_thread&, vm::cptr<char> path, s32 mode)
{
// Try to locate split files
for (u32 i = 66601; i <= 66699; i++)
{
if (mp != &g_mp_sys_dev_hdd1 && !fs::get_stat(fmt::format("%s.%u", local_path, i), info) && !info.is_directory)
{
break;
}
}
if (fs::get_stat(local_path + ".66600", info) && !info.is_directory)
{
break;

View File

@ -67,7 +67,6 @@ u32 sys_gamepad_ycon_is_gem(vm::ptr<u8> in, vm::ptr<u8> out)
// syscall(621,packet_id,u8 *in,u8 *out) Talk:LV2_Functions_and_Syscalls#Syscall_621_.280x26D.29 gamepad_if usage
u32 sys_gamepad_ycon_if(u8 packet_id, vm::ptr<u8> in, vm::ptr<u8> out)
{
switch (packet_id)
{
case 0:

View File

@ -487,6 +487,8 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
{
ensure(cond.unqueue(cond.sq, &ppu));
ppu.state += cpu_flag::again;
cond.lwmutex_waiters--;
mutex->lwcond_waiters--;
return;
}

View File

@ -15,6 +15,18 @@ LOG_CHANNEL(sys_memory);
//
static shared_mutex s_memstats_mtx;
// This struct is for reduced logging repetition
struct last_reported_memory_stats
{
struct inner_body
{
u32 prev_total = umax;
u32 prev_avail = umax;
};
atomic_t<inner_body> body{};
};
lv2_memory_container::lv2_memory_container(u32 size, bool from_idm) noexcept
: size(size)
, id{from_idm ? idm::last_id() : SYS_MEMORY_CONTAINER_ID_INVALID}
@ -313,8 +325,6 @@ error_code sys_memory_get_user_memory_size(cpu_thread& cpu, vm::ptr<sys_memory_i
{
cpu.state += cpu_flag::wait;
sys_memory.warning("sys_memory_get_user_memory_size(mem_info=*0x%x)", mem_info);
// Get "default" memory container
auto& dct = g_fxo->get<lv2_memory_container>();
@ -332,6 +342,22 @@ error_code sys_memory_get_user_memory_size(cpu_thread& cpu, vm::ptr<sys_memory_i
});
}
typename last_reported_memory_stats::inner_body now;
now.prev_total = out.total_user_memory;
now.prev_avail = out.available_user_memory;
now = g_fxo->get<last_reported_memory_stats>().body.exchange(now);
if (now.prev_total != out.total_user_memory || now.prev_avail != out.available_user_memory)
{
// Log on change
sys_memory.warning("sys_memory_get_user_memory_size(mem_info=*0x%x): Avail=0x%x, Total=0x%x", mem_info, out.available_user_memory, out.total_user_memory);
}
else
{
sys_memory.trace("sys_memory_get_user_memory_size(mem_info=*0x%x): Avail=0x%x, Total=0x%x", mem_info, out.available_user_memory, out.total_user_memory);
}
cpu.check_state();
*mem_info = out;
return CELL_OK;

View File

@ -333,7 +333,7 @@ error_code sys_mmapper_allocate_shared_memory_ext(ppu_thread& ppu, u64 ipc_key,
}
}
if (flags & ~SYS_MEMORY_PAGE_SIZE_MASK)
if (flags & ~SYS_MEMORY_GRANULARITY_MASK)
{
return CELL_EINVAL;
}
@ -401,6 +401,11 @@ error_code sys_mmapper_allocate_shared_memory_from_container_ext(ppu_thread& ppu
sys_mmapper.todo("sys_mmapper_allocate_shared_memory_from_container_ext(ipc_key=0x%x, size=0x%x, flags=0x%x, cid=0x%x, entries=*0x%x, entry_count=0x%x, mem_id=*0x%x)", ipc_key, size, flags, cid, entries,
entry_count, mem_id);
if (size == 0)
{
return CELL_EALIGN;
}
switch (flags & SYS_MEMORY_PAGE_SIZE_MASK)
{
case SYS_MEMORY_PAGE_SIZE_1M:
@ -546,8 +551,7 @@ error_code sys_mmapper_free_address(ppu_thread& ppu, u32 addr)
// If a memory block is freed, remove it from page notification table.
auto& pf_entries = g_fxo->get<page_fault_notification_entries>();
std::lock_guard lock(pf_entries.mutex);
std::unique_lock lock(pf_entries.mutex);
auto ind_to_remove = pf_entries.entries.begin();
for (; ind_to_remove != pf_entries.entries.end(); ++ind_to_remove)
{
@ -558,7 +562,11 @@ error_code sys_mmapper_free_address(ppu_thread& ppu, u32 addr)
}
if (ind_to_remove != pf_entries.entries.end())
{
u32 port_id = ind_to_remove->port_id;
pf_entries.entries.erase(ind_to_remove);
lock.unlock();
sys_event_port_disconnect(ppu, port_id);
sys_event_port_destroy(ppu, port_id);
}
return CELL_OK;
@ -826,7 +834,6 @@ error_code sys_mmapper_enable_page_fault_notification(ppu_thread& ppu, u32 start
vm::var<u32> port_id(0);
error_code res = sys_event_port_create(ppu, port_id, SYS_EVENT_PORT_LOCAL, SYS_MEMORY_PAGE_FAULT_EVENT_KEY);
sys_event_port_connect_local(ppu, *port_id, event_queue_id);
if (res + 0u == CELL_EAGAIN)
{
@ -834,6 +841,8 @@ error_code sys_mmapper_enable_page_fault_notification(ppu_thread& ppu, u32 start
return CELL_EAGAIN;
}
sys_event_port_connect_local(ppu, *port_id, event_queue_id);
auto& pf_entries = g_fxo->get<page_fault_notification_entries>();
std::unique_lock lock(pf_entries.mutex);

View File

@ -85,7 +85,7 @@ error_code sys_mutex_create(ppu_thread& ppu, vm::ptr<u32> mutex_id, vm::ptr<sys_
sys_mutex.todo("sys_mutex_create(): unexpected adaptive (0x%x)", _attr.adaptive);
}
if (auto error = lv2_obj::create<lv2_mutex>(_attr.pshared, _attr.ipc_key, _attr.flags, [&]()
if (auto error = lv2_obj::create<lv2_mutex>(_attr.pshared, ipc_key, _attr.flags, [&]()
{
return make_shared<lv2_mutex>(
_attr.protocol,

View File

@ -173,7 +173,11 @@ struct lv2_mutex final : lv2_obj
if (sq == data.sq)
{
atomic_storage<u32>::release(control.raw().owner, res->id);
if (cpu_flag::again - res->state)
{
atomic_storage<u32>::release(control.raw().owner, res->id);
}
return false;
}

View File

@ -563,37 +563,34 @@ error_code sys_net_bnet_connect(ppu_thread& ppu, s32 s, vm::ptr<sys_net_sockaddr
return not_an_error(result);
}
if (!sock.ret)
while (auto state = ppu.state.fetch_sub(cpu_flag::signal))
{
while (auto state = ppu.state.fetch_sub(cpu_flag::signal))
if (is_stopped(state))
{
if (is_stopped(state))
{
return {};
}
if (state & cpu_flag::signal)
{
break;
}
ppu.state.wait(state);
return {};
}
if (ppu.gpr[3] == static_cast<u64>(-SYS_NET_EINTR))
if (state & cpu_flag::signal)
{
return -SYS_NET_EINTR;
break;
}
if (result)
{
if (result < 0)
{
return sys_net_error{result};
}
ppu.state.wait(state);
}
return not_an_error(result);
if (ppu.gpr[3] == static_cast<u64>(-SYS_NET_EINTR))
{
return -SYS_NET_EINTR;
}
if (result)
{
if (result < 0)
{
return sys_net_error{result};
}
return not_an_error(result);
}
return CELL_OK;
@ -992,7 +989,7 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
fmt::throw_exception("sys_net_bnet_sendto(s=%d): unknown flags (0x%x)", flags);
}
if (addr && addrlen < 8)
if (addr && addrlen < sizeof(sys_net_sockaddr))
{
sys_net.error("sys_net_bnet_sendto(s=%d): bad addrlen (%u)", s, addrlen);
return -SYS_NET_EINVAL;
@ -1295,7 +1292,7 @@ error_code sys_net_bnet_poll(ppu_thread& ppu, vm::ptr<sys_net_pollfd> fds, s32 n
if (auto sock = idm::check_unlocked<lv2_socket>(fds_buf[i].fd))
{
signaled += sock->poll(fds_buf[i], _fds[i]);
sock->poll(fds_buf[i], _fds[i]);
#ifdef _WIN32
connecting[i] = sock->is_connecting();
#endif
@ -1303,7 +1300,6 @@ error_code sys_net_bnet_poll(ppu_thread& ppu, vm::ptr<sys_net_pollfd> fds, s32 n
else
{
fds_buf[i].revents |= SYS_NET_POLLNVAL;
signaled++;
}
}
@ -1536,9 +1532,9 @@ error_code sys_net_bnet_select(ppu_thread& ppu, s32 nfds, vm::ptr<sys_net_fd_set
for (s32 i = 0; i < nfds; i++)
{
bool sig = false;
if (_fds[i].revents & (POLLIN | POLLHUP | POLLERR))
if ((_fds[i].revents & (POLLIN | POLLHUP | POLLERR)) && _readfds.bit(i))
sig = true, rread.set(i);
if (_fds[i].revents & (POLLOUT | POLLERR))
if ((_fds[i].revents & (POLLOUT | POLLERR)) && _writefds.bit(i))
sig = true, rwrite.set(i);
if (sig)

View File

@ -107,7 +107,7 @@ enum lv2_ip_option : s32
SYS_NET_IP_MULTICAST_LOOP = 11,
SYS_NET_IP_ADD_MEMBERSHIP = 12,
SYS_NET_IP_DROP_MEMBERSHIP = 13,
SYS_NET_IP_TTLCHK = 23,
SYS_NET_IP_TTLCHK = 23, // This is probably the equivalent of IP_MINTTL on FreeBSD
SYS_NET_IP_MAXTTL = 24,
SYS_NET_IP_DONTFRAG = 26
};

View File

@ -104,7 +104,7 @@ public:
virtual void close() = 0;
virtual s32 shutdown(s32 how) = 0;
virtual s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) = 0;
virtual void poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) = 0;
virtual std::tuple<bool, bool, bool> select(bs_t<poll_t> selected, pollfd& native_pfd) = 0;
error_code abort_socket(s32 flags);

View File

@ -551,12 +551,14 @@ std::tuple<s32, lv2_socket::sockopt_data, u32> lv2_socket_native::getsockopt(s32
}
case SYS_NET_IP_TTLCHK:
{
sys_net.error("sys_net_bnet_getsockopt(IPPROTO_IP, SYS_NET_IP_TTLCHK): stubbed option");
out_val._int = min_ttl;
out_len = sizeof(s32);
return {CELL_OK, out_val, out_len};
}
case SYS_NET_IP_MAXTTL:
{
sys_net.error("sys_net_bnet_getsockopt(IPPROTO_IP, SYS_NET_IP_MAXTTL): stubbed option");
out_val._int = max_ttl;
out_len = sizeof(s32);
return {CELL_OK, out_val, out_len};
}
case SYS_NET_IP_DONTFRAG:
@ -834,13 +836,13 @@ s32 lv2_socket_native::setsockopt(s32 level, s32 optname, const std::vector<u8>&
}
case SYS_NET_IP_TTLCHK:
{
sys_net.error("sys_net_bnet_setsockopt(s=%d, IPPROTO_IP): Stubbed option (0x%x) (SYS_NET_IP_TTLCHK)", lv2_id, optname);
break;
min_ttl = native_int;
return {};
}
case SYS_NET_IP_MAXTTL:
{
sys_net.error("sys_net_bnet_setsockopt(s=%d, IPPROTO_IP): Stubbed option (0x%x) (SYS_NET_IP_MAXTTL)", lv2_id, optname);
break;
max_ttl = native_int;
return {};
}
case SYS_NET_IP_DONTFRAG:
{
@ -910,7 +912,7 @@ std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>> lv2_socket_nat
{
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
const auto packet = dnshook.get_dns_packet(lv2_id);
ensure(packet.size() < len);
ensure(packet.size() <= len);
memcpy(res_buf.data(), packet.data(), packet.size());
native_addr.ss_family = AF_INET;
(reinterpret_cast<::sockaddr_in*>(&native_addr))->sin_port = std::bit_cast<u16, be_t<u16>>(53); // htons(53)
@ -1069,18 +1071,20 @@ std::optional<s32> lv2_socket_native::sendmsg(s32 flags, const sys_net_msghdr& m
return {-SYS_NET_ECONNRESET};
}
std::vector<u8> buf_copy;
for (int i = 0; i < msg.msg_iovlen; i++)
{
auto iov_base = msg.msg_iov[i].iov_base;
const u32 len = msg.msg_iov[i].iov_len;
const std::vector<u8> buf_copy(vm::_ptr<const char>(iov_base.addr()), vm::_ptr<const char>(iov_base.addr()) + len);
const auto* src = vm::_ptr<const char>(iov_base.addr());
buf_copy.insert(buf_copy.end(), src, src + len);
}
native_result = ::send(native_socket, reinterpret_cast<const char*>(buf_copy.data()), ::narrow<int>(buf_copy.size()), native_flags);
native_result = ::send(native_socket, reinterpret_cast<const char*>(buf_copy.data()), ::narrow<int>(buf_copy.size()), native_flags);
if (native_result >= 0)
{
return {native_result};
}
if (native_result >= 0)
{
return {native_result};
}
result = get_last_error(!so_nbio && (flags & SYS_NET_MSG_DONTWAIT) == 0);
@ -1143,14 +1147,14 @@ s32 lv2_socket_native::shutdown(s32 how)
return -get_last_error(false);
}
s32 lv2_socket_native::poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd)
void lv2_socket_native::poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd)
{
// Check for fake packet for dns interceptions
auto& dnshook = g_fxo->get<np::dnshook>();
if (sn_pfd.events & SYS_NET_POLLIN && dnshook.is_dns(sn_pfd.fd) && dnshook.is_dns_queue(sn_pfd.fd))
{
sn_pfd.revents |= SYS_NET_POLLIN;
return 1;
return;
}
if (sn_pfd.events & ~(SYS_NET_POLLIN | SYS_NET_POLLOUT | SYS_NET_POLLERR))
{
@ -1167,8 +1171,6 @@ s32 lv2_socket_native::poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd)
{
native_pfd.events |= POLLOUT;
}
return 0;
}
std::tuple<bool, bool, bool> lv2_socket_native::select(bs_t<lv2_socket::poll_t> selected, pollfd& native_pfd)
@ -1232,16 +1234,16 @@ bool lv2_socket_native::is_socket_connected()
return false;
}
fd_set readfds, writefds;
struct timeval timeout{0, 0}; // Zero timeout
pollfd pfd{};
pfd.fd = native_socket;
pfd.events = POLLIN | POLLOUT;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_SET(native_socket, &readfds);
FD_SET(native_socket, &writefds);
// Use select to check for readability and writability
const int result = ::select(1, &readfds, &writefds, NULL, &timeout);
// Use poll to check for readability and writability
#ifdef _WIN32
const int result = WSAPoll(&pfd, 1, 0);
#else
const int result = ::poll(&pfd, 1, 0);
#endif
if (result < 0)
{
@ -1250,5 +1252,5 @@ bool lv2_socket_native::is_socket_connected()
}
// Socket is connected if it's readable or writable
return FD_ISSET(native_socket, &readfds) || FD_ISSET(native_socket, &writefds);
return (pfd.revents & (POLLIN | POLLOUT)) != 0;
}

View File

@ -50,7 +50,7 @@ public:
std::optional<s32> sendto(s32 flags, const std::vector<u8>& buf, std::optional<sys_net_sockaddr> opt_sn_addr, bool is_lock = true) override;
std::optional<s32> sendmsg(s32 flags, const sys_net_msghdr& msg, bool is_lock = true) override;
s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
void poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
std::tuple<bool, bool, bool> select(bs_t<poll_t> selected, pollfd& native_pfd) override;
bool is_socket_connected();
@ -70,6 +70,10 @@ private:
s32 so_reuseaddr = 0;
s32 so_reuseport = 0;
#endif
// Those values come from FreeBSD
s32 min_ttl = 1;
s32 max_ttl = 64;
u16 bound_port = 0;
bool feign_tcp_conn_failure = false; // Savestate load related
};

View File

@ -364,7 +364,7 @@ s32 lv2_socket_p2p::shutdown([[maybe_unused]] s32 how)
return CELL_OK;
}
s32 lv2_socket_p2p::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native_pfd)
void lv2_socket_p2p::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native_pfd)
{
std::lock_guard lock(mutex);
ensure(vport);
@ -381,8 +381,6 @@ s32 lv2_socket_p2p::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native
{
sn_pfd.revents |= SYS_NET_POLLOUT;
}
return sn_pfd.revents ? 1 : 0;
}
std::tuple<bool, bool, bool> lv2_socket_p2p::select(bs_t<lv2_socket::poll_t> selected, [[maybe_unused]] pollfd& native_pfd)

View File

@ -30,7 +30,7 @@ public:
void close() override;
s32 shutdown(s32 how) override;
s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
void poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
std::tuple<bool, bool, bool> select(bs_t<poll_t> selected, pollfd& native_pfd) override;
void handle_new_data(sys_net_sockaddr_in_p2p p2p_addr, std::vector<u8> p2p_data);

View File

@ -112,7 +112,6 @@ public:
// reply is late, increases rtt
auto& msg = it->second;
const auto addr = msg.dst_addr.sin_addr.s_addr;
rtt_info rtt = rtts[msg.sock_id];
// Only increases rtt once per loop(in case a big number of packets are sent at once)
if (!rtt_increased.count(msg.sock_id))
@ -120,7 +119,7 @@ public:
rtt.num_retries += 1;
// Increases current rtt by 10%
rtt.rtt_time += (rtt.rtt_time / 10);
rtts[addr] = rtt;
rtts[msg.sock_id] = rtt;
rtt_increased.emplace(msg.sock_id);
}
@ -625,7 +624,7 @@ std::tuple<bool, s32, shared_ptr<lv2_socket>, sys_net_sockaddr> lv2_socket_p2ps:
sys_net_sockaddr ps3_addr{};
auto* paddr = reinterpret_cast<sys_net_sockaddr_in_p2p*>(&ps3_addr);
lv2_socket_p2ps* sock_client = reinterpret_cast<lv2_socket_p2ps*>(idm::check_unlocked<lv2_socket>(p2ps_client));
auto sock_client = static_cast<shared_ptr<lv2_socket_p2ps>>(idm::get_unlocked<lv2_socket>(p2ps_client));
{
std::lock_guard lock(sock_client->mutex);
paddr->sin_family = SYS_NET_AF_INET;
@ -986,7 +985,7 @@ s32 lv2_socket_p2ps::shutdown([[maybe_unused]] s32 how)
return CELL_OK;
}
s32 lv2_socket_p2ps::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native_pfd)
void lv2_socket_p2ps::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native_pfd)
{
std::lock_guard lock(mutex);
sys_net.trace("[P2PS] poll checking for 0x%X", sn_pfd.events);
@ -1003,14 +1002,7 @@ s32 lv2_socket_p2ps::poll(sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& nativ
{
sn_pfd.revents |= SYS_NET_POLLOUT;
}
if (sn_pfd.revents)
{
return 1;
}
}
return 0;
}
std::tuple<bool, bool, bool> lv2_socket_p2ps::select(bs_t<lv2_socket::poll_t> selected, [[maybe_unused]] pollfd& native_pfd)

View File

@ -89,7 +89,7 @@ public:
void close() override;
s32 shutdown(s32 how) override;
s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
void poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
std::tuple<bool, bool, bool> select(bs_t<poll_t> selected, pollfd& native_pfd) override;
private:

View File

@ -134,10 +134,9 @@ s32 lv2_socket_raw::shutdown([[maybe_unused]] s32 how)
return {};
}
s32 lv2_socket_raw::poll([[maybe_unused]] sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native_pfd)
void lv2_socket_raw::poll([[maybe_unused]] sys_net_pollfd& sn_pfd, [[maybe_unused]] pollfd& native_pfd)
{
LOG_ONCE(raw_poll, "lv2_socket_raw::poll");
return {};
}
std::tuple<bool, bool, bool> lv2_socket_raw::select([[maybe_unused]] bs_t<lv2_socket::poll_t> selected, [[maybe_unused]] pollfd& native_pfd)

View File

@ -32,6 +32,6 @@ public:
void close() override;
s32 shutdown(s32 how) override;
s32 poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
void poll(sys_net_pollfd& sn_pfd, pollfd& native_pfd) override;
std::tuple<bool, bool, bool> select(bs_t<poll_t> selected, pollfd& native_pfd) override;
};

View File

@ -249,8 +249,9 @@ bool nt_p2p_port::recv_data()
auto& bound_sockets = ::at32(bound_p2p_vports, dst_vport);
for (const auto sock_id : bound_sockets)
for (auto it = bound_sockets.begin(); it != bound_sockets.end();)
{
s32 sock_id = *it;
const auto sock = idm::check<lv2_socket>(sock_id, [&](lv2_socket& sock)
{
ensure(sock.get_type() == SYS_NET_SOCK_DGRAM_P2P);
@ -262,12 +263,17 @@ bool nt_p2p_port::recv_data()
if (!sock)
{
sys_net.error("Socket %d found in bound_p2p_vports didn't exist!", sock_id);
bound_sockets.erase(sock_id);
it = bound_sockets.erase(it);
if (bound_sockets.empty())
{
bound_p2p_vports.erase(dst_vport);
break;
}
}
else
{
it++;
}
}
return true;

View File

@ -305,6 +305,7 @@ error_code sys_ppu_thread_detach(ppu_thread& ppu, u32 thread_id)
{
// Join and notify thread (it is detached from IDM now so it must be done explicitly now)
*ptr = thread_state::finished;
return CELL_OK;
}
return result;

View File

@ -64,7 +64,7 @@ extern const std::map<std::string_view, int> g_prx_list
{ "libddpdec.sprx", 0 },
{ "libdivxdec.sprx", 0 },
{ "libdmux.sprx", 0 },
{ "libdmuxpamf.sprx", 0 },
{ "libdmuxpamf.sprx", 1 },
{ "libdtslbrdec.sprx", 0 },
{ "libfiber.sprx", 0 },
{ "libfont.sprx", 0 },
@ -899,7 +899,7 @@ error_code _sys_prx_register_library(ppu_thread& ppu, vm::ptr<void> library)
{
for (u32 lib_addr = prx.exports_start, index = 0; lib_addr < prx.exports_end; index++, lib_addr += vm::read8(lib_addr) ? vm::read8(lib_addr) : sizeof_lib)
{
if (std::memcpy(vm::base(lib_addr), mem_copy.data(), sizeof_lib) == 0)
if (std::memcmp(vm::base(lib_addr), mem_copy.data(), sizeof_lib) == 0)
{
atomic_storage<char>::release(prx.m_external_loaded_flags[index], true);
return true;

View File

@ -46,14 +46,14 @@ namespace rsxaudio_ringbuf_reader
static void set_timestamp(rsxaudio_shmem::ringbuf_t& ring_buf, u64 timestamp)
{
const s32 entry_idx_raw = (ring_buf.read_idx + ring_buf.rw_max_idx - (ring_buf.rw_max_idx > 2) - 1) % ring_buf.rw_max_idx;
const s32 entry_idx = std::clamp<s32>(entry_idx_raw, 0, SYS_RSXAUDIO_RINGBUF_SZ);
const s32 entry_idx = std::clamp<s32>(entry_idx_raw, 0, SYS_RSXAUDIO_RINGBUF_SZ - 1);
ring_buf.entries[entry_idx].timestamp = convert_to_timebased_time(timestamp);
}
static std::tuple<bool /*notify*/, u64 /*blk_idx*/, u64 /*timestamp*/> update_status(rsxaudio_shmem::ringbuf_t& ring_buf)
{
const s32 read_idx = std::clamp<s32>(ring_buf.read_idx, 0, SYS_RSXAUDIO_RINGBUF_SZ);
const s32 read_idx = std::clamp<s32>(ring_buf.read_idx, 0, SYS_RSXAUDIO_RINGBUF_SZ - 1);
if ((ring_buf.entries[read_idx].valid & 1) == 0U)
{
@ -61,7 +61,7 @@ namespace rsxaudio_ringbuf_reader
}
const s32 entry_idx_raw = (ring_buf.read_idx + ring_buf.rw_max_idx - (ring_buf.rw_max_idx > 2)) % ring_buf.rw_max_idx;
const s32 entry_idx = std::clamp<s32>(entry_idx_raw, 0, SYS_RSXAUDIO_RINGBUF_SZ);
const s32 entry_idx = std::clamp<s32>(entry_idx_raw, 0, SYS_RSXAUDIO_RINGBUF_SZ - 1);
ring_buf.entries[read_idx].valid = 0;
ring_buf.queue_notify_idx = (ring_buf.queue_notify_idx + 1) % ring_buf.queue_notify_step;
@ -72,7 +72,7 @@ namespace rsxaudio_ringbuf_reader
static std::pair<bool /*entry_valid*/, u32 /*addr*/> get_addr(const rsxaudio_shmem::ringbuf_t& ring_buf)
{
const s32 read_idx = std::clamp<s32>(ring_buf.read_idx, 0, SYS_RSXAUDIO_RINGBUF_SZ);
const s32 read_idx = std::clamp<s32>(ring_buf.read_idx, 0, SYS_RSXAUDIO_RINGBUF_SZ - 1);
if (ring_buf.entries[read_idx].valid & 1)
{
@ -1392,9 +1392,9 @@ void rsxaudio_backend_thread::operator()()
return;
}
static rsxaudio_state ra_state{};
static emu_audio_cfg emu_cfg{};
static bool backend_failed = false;
rsxaudio_state ra_state{};
emu_audio_cfg emu_cfg{};
bool backend_failed = false;
for (;;)
{
@ -2018,7 +2018,7 @@ void rsxaudio_periodic_tmr::cancel_timer_unlocked()
{
const u64 flag = 1;
const auto wr_res = write(cancel_event, &flag, sizeof(flag));
ensure(wr_res == sizeof(flag) || wr_res == -EAGAIN);
ensure(wr_res == sizeof(flag) || errno == EAGAIN);
}
#elif defined(BSD) || defined(__APPLE__)
handle[TIMER_ID].flags = (handle[TIMER_ID].flags & ~EV_ENABLE) | EV_DISABLE;

View File

@ -441,6 +441,8 @@ error_code sys_rwlock_wlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout)
continue;
}
ppu.state += cpu_flag::wait;
std::lock_guard lock(rwlock->mutex);
if (!rwlock->unqueue(rwlock->wq, &ppu))

View File

@ -72,7 +72,7 @@ error_code sys_semaphore_create(ppu_thread& ppu, vm::ptr<u32> sem_id, vm::ptr<sy
return error;
}
static_cast<void>(ppu.test_stopped());
ppu.check_state();
*sem_id = idm::last_id();
return CELL_OK;
@ -358,7 +358,7 @@ error_code sys_semaphore_get_value(ppu_thread& ppu, u32 sem_id, vm::ptr<s32> cou
return CELL_EFAULT;
}
static_cast<void>(ppu.test_stopped());
ppu.check_state();
*count = sema.ret;
return CELL_OK;

View File

@ -437,7 +437,7 @@ struct spu_limits_t
raw_spu_count += spu_thread::g_raw_spu_ctr;
// physical_spus_count >= spu_limit returns EBUSY, not EINVAL!
if (spu_limit + raw_limit > 6 || raw_spu_count > raw_limit || physical_spus_count >= spu_limit || physical_spus_count > spu_limit || controllable_spu_count > spu_limit)
if (spu_limit + raw_limit > 6 || raw_spu_count > raw_limit || physical_spus_count >= spu_limit || controllable_spu_count > spu_limit)
{
return false;
}

View File

@ -24,7 +24,7 @@ struct lv2_update_manager
// For example, 4.90 should be converted to 0x4900000000000
std::erase(version_str, '.');
if (std::from_chars(version_str.data(), version_str.data() + version_str.size(), system_sw_version, 16).ec != std::errc{})
if (std::from_chars(version_str.data(), version_str.data() + version_str.size(), system_sw_version, 16).ec == std::errc{})
system_sw_version <<= 40;
else
system_sw_version = 0;
@ -79,6 +79,7 @@ struct lv2_update_manager
if (malloc_set.count(addr))
{
malloc_set.erase(addr);
return vm::dealloc(addr, vm::main);
}

View File

@ -453,6 +453,7 @@ public:
// Can be called before the actual sleep call in order to move it out of mutex scope
static void prepare_for_sleep(cpu_thread& cpu);
static ppu_thread* get_running_ppu(u32 index);
struct notify_all_t
{

View File

@ -118,6 +118,7 @@ static int clock_gettime(int clk_id, struct timespec* tp)
#ifndef _WIN32
#include <exception>
#include <sys/time.h>
static struct timespec start_time = []()

View File

@ -1531,9 +1531,11 @@ private:
}
}();
if (sce_idx == umax)
return PS3AV_STATUS_INVALID_VIDEO_PARAM;
const video_sce_param &sce_param = sce_param_arr[sce_idx];
if (sce_idx == umax ||
video_head_cfg.video_head > PS3AV_HEAD_B_ANALOG ||
if (video_head_cfg.video_head > PS3AV_HEAD_B_ANALOG ||
video_head_cfg.video_order > 1 ||
video_head_cfg.video_format > 16 ||
video_head_cfg.video_out_format > 16 ||

View File

@ -1173,11 +1173,15 @@ error_code sys_usbd_get_device_list(ppu_thread& ppu, u32 handle, vm::ptr<UsbInte
return CELL_EINVAL;
// TODO: was std::min<s32>
u32 i_tocopy = std::min<u32>(max_devices, ::size32(usbh.handled_devices));
const u32 i_tocopy = std::min<u32>(max_devices, ::size32(usbh.handled_devices));
u32 index = 0;
for (u32 index = 0; index < i_tocopy; index++)
for (const auto& [_, device] : usbh.handled_devices)
{
device_list[index] = usbh.handled_devices[index].first;
if (index == i_tocopy)
break;
device_list[index++] = device.first;
}
return not_an_error(i_tocopy);
@ -1409,7 +1413,7 @@ error_code sys_usbd_receive_event(ppu_thread& ppu, u32 handle, vm::ptr<u64> arg1
if (is_stopped(state))
{
std::lock_guard lock(usbh.mutex);
std::lock_guard lock(usbh.mutex_sq);
for (auto cpu = +usbh.sq; cpu; cpu = cpu->next_cpu)
{
@ -1587,7 +1591,7 @@ error_code sys_usbd_get_transfer_status(ppu_thread& ppu, u32 handle, u32 id_tran
std::lock_guard lock(usbh.mutex);
if (!usbh.is_init)
if (!usbh.is_init || id_transfer >= MAX_SYS_USBD_TRANSFERS)
return CELL_EINVAL;
const auto status = usbh.get_transfer_status(id_transfer);
@ -1607,7 +1611,7 @@ error_code sys_usbd_get_isochronous_transfer_status(ppu_thread& ppu, u32 handle,
std::lock_guard lock(usbh.mutex);
if (!usbh.is_init)
if (!usbh.is_init || id_transfer >= MAX_SYS_USBD_TRANSFERS)
return CELL_EINVAL;
const auto status = usbh.get_isochronous_transfer_status(id_transfer);

View File

@ -97,7 +97,7 @@ error_code sys_vm_memory_map(ppu_thread& ppu, u64 vsize, u64 psize, u32 cid, u64
// Look for unmapped space
if (const auto area = vm::find_map(0x10000000, 0x10000000, 2 | (flag & SYS_MEMORY_PAGE_SIZE_MASK)))
{
sys_vm.warning("sys_vm_memory_map(): Found VM 0x%x area (vsize=0x%x)", addr, vsize);
sys_vm.warning("sys_vm_memory_map(): Found VM 0x%x area (vsize=0x%x)", area->addr, vsize);
// Alloc all memory (shall not fail)
ensure(area->alloc(static_cast<u32>(vsize)));

View File

@ -8,6 +8,7 @@ struct GameInfo
std::string path;
std::string icon_path;
std::string movie_path;
std::string audio_path;
std::string name;
std::string serial;

View File

@ -945,17 +945,6 @@ static u8 sdl_to_logitech_g27_pedal(std::map<u64, std::vector<SDL_Joystick*>>& j
return unsigned_avg * 0xFF / 0xFFFF;
}
static inline void set_bit(u8* buf, int bit_num, bool set)
{
const int byte_num = bit_num / 8;
bit_num %= 8;
const u8 mask = 1 << bit_num;
if (set)
buf[byte_num] = buf[byte_num] | mask;
else
buf[byte_num] = buf[byte_num] & (~mask);
}
void usb_device_logitech_g27::transfer_dfex(u32 buf_size, u8* buf, UsbTransfer* transfer)
{
DFEX_data data{};

View File

@ -38,10 +38,7 @@ struct logitech_g27_ffb_slot
logitech_g27_ffb_state state = logitech_g27_ffb_state::inactive;
u64 last_update = 0;
SDL_HapticEffect last_effect {};
// TODO switch to SDL_HapticEffectID when it becomes available in a future SDL release
// Match the return of SDL_CreateHapticEffect for now
int effect_id = -1;
SDL_HapticEffectID effect_id = -1;
};
struct sdl_mapping

View File

@ -24,13 +24,11 @@ std::set<u32> PadHandlerBase::narrow_set(const std::set<u64>& src)
return dst;
}
// Get new multiplied value based on the multiplier
s32 PadHandlerBase::MultipliedInput(s32 raw_value, s32 multiplier)
{
return (multiplier * raw_value) / 100;
}
// Get new scaled value between 0 and range based on its minimum and maximum
f32 PadHandlerBase::ScaledInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range)
{
if (deadzone > 0 && deadzone > minimum)
@ -46,7 +44,6 @@ f32 PadHandlerBase::ScaledInput(f32 raw_value, f32 minimum, f32 maximum, f32 dea
return range * val;
}
// Get new scaled value between -range and range based on its minimum and maximum
f32 PadHandlerBase::ScaledAxisInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range)
{
// convert [min, max] to [0, 1]
@ -79,7 +76,6 @@ f32 PadHandlerBase::ScaledAxisInput(f32 raw_value, f32 minimum, f32 maximum, f32
return (2.0f * range * val) - range;
}
// Get normalized trigger value based on the range defined by a threshold
u16 PadHandlerBase::NormalizeTriggerInput(u16 value, u32 threshold) const
{
if (value <= threshold || threshold >= trigger_max)
@ -90,8 +86,6 @@ u16 PadHandlerBase::NormalizeTriggerInput(u16 value, u32 threshold) const
return static_cast<u16>(ScaledInput(static_cast<f32>(value), static_cast<f32>(trigger_min), static_cast<f32>(trigger_max), static_cast<f32>(threshold)));
}
// normalizes a directed input, meaning it will correspond to a single "button" and not an axis with two directions
// the input values must lie in 0+
u16 PadHandlerBase::NormalizeDirectedInput(s32 raw_value, s32 threshold, s32 maximum) const
{
if (threshold >= maximum || maximum <= 0 || raw_value < 0)
@ -114,9 +108,6 @@ u16 PadHandlerBase::NormalizeStickInput(u16 raw_value, s32 threshold, s32 multip
return static_cast<u16>(ScaledInput(static_cast<f32>(scaled_value), 0.0f, static_cast<f32>(thumb_max), static_cast<f32>(threshold)));
}
// This function normalizes stick deadzone based on the DS3's deadzone, which is ~13% (default of anti deadzone)
// X and Y is expected to be in (-255) to 255 range, deadzone should be in terms of thumb stick range
// return is new x and y values in 0-255 range
std::tuple<u16, u16> PadHandlerBase::NormalizeStickDeadzone(s32 inX, s32 inY, u32 deadzone, u32 anti_deadzone) const
{
f32 X = inX / 255.0f;
@ -150,28 +141,21 @@ std::tuple<u16, u16> PadHandlerBase::NormalizeStickDeadzone(s32 inX, s32 inY, u3
return std::tuple<u16, u16>(ConvertAxis(X), ConvertAxis(Y));
}
// get clamped value between 0 and 255
u16 PadHandlerBase::Clamp0To255(f32 input)
{
return static_cast<u16>(std::clamp(input, 0.0f, 255.0f));
}
// get clamped value between 0 and 1023
u16 PadHandlerBase::Clamp0To1023(f32 input)
{
return static_cast<u16>(std::clamp(input, 0.0f, 1023.0f));
}
// input has to be [-1,1]. result will be [0,255]
u16 PadHandlerBase::ConvertAxis(f32 value)
{
return static_cast<u16>((value + 1.0) * (255.0 / 2.0));
}
// The DS3, (and i think xbox controllers) give a 'square-ish' type response, so that the corners will give (almost)max x/y instead of the ~30x30 from a perfect circle
// using a simple scale/sensitivity increase would *work* although it eats a chunk of our usable range in exchange
// this might be the best for now, in practice it seems to push the corners to max of 20x20, with a squircle_factor of ~4000
// This function assumes inX and inY is already in 0-255
void PadHandlerBase::ConvertToSquirclePoint(u16& inX, u16& inY, u32 squircle_factor)
{
if (!squircle_factor)

View File

@ -274,7 +274,7 @@ protected:
// the input values must lie in 0+
u16 NormalizeDirectedInput(s32 raw_value, s32 threshold, s32 maximum) const;
// This function normalizes stick deadzone based on the DS3's deadzone, which is ~13%
// This function normalizes stick deadzone based on the DS3's deadzone, which is ~13% (default of anti deadzone)
// X and Y is expected to be in (-255) to 255 range, deadzone should be in terms of thumb stick range
// return is new x and y values in 0-255 range
std::tuple<u16, u16> NormalizeStickDeadzone(s32 inX, s32 inY, u32 deadzone, u32 anti_deadzone) const;
@ -284,10 +284,10 @@ public:
// Get new multiplied value based on the multiplier
static s32 MultipliedInput(s32 raw_value, s32 multiplier);
// Get new scaled value between 0 and 255 based on its minimum and maximum
// Get new scaled value between 0 and range based on its minimum and maximum
static f32 ScaledInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range = 255.0f);
// Get new scaled value between -255 and 255 based on its minimum and maximum
// Get new scaled value between -range and range based on its minimum and maximum
static f32 ScaledAxisInput(f32 raw_value, f32 minimum, f32 maximum, f32 deadzone, f32 range = 255.0f);
// get clamped value between 0 and 255
@ -301,7 +301,7 @@ public:
// The DS3, (and i think xbox controllers) give a 'square-ish' type response, so that the corners will give (almost)max x/y instead of the ~30x30 from a perfect circle
// using a simple scale/sensitivity increase would *work* although it eats a chunk of our usable range in exchange
// this might be the best for now, in practice it seems to push the corners to max of 20x20, with a squircle_factor of 8000
// this might be the best for now, in practice it seems to push the corners to max of 20x20, with a squircle_factor of ~4000
// This function assumes inX and inY is already in 0-255
static void ConvertToSquirclePoint(u16& inX, u16& inY, u32 squircle_factor);

View File

@ -24,34 +24,34 @@ void ps_move_data::reset_sensors()
angaccel_world = {};
}
ps_move_data::vect<3> ps_move_data::rotate_vector(const vect<4>& q, const vect<3>& v)
{
const auto cross = [](const vect<3>& a, const vect<3>& b)
{
return vect<3>({
a.y() * b.z() - a.z() * b.y(),
a.z() * b.x() - a.x() * b.z(),
a.x() * b.y() - a.y() * b.x()
});
};
// q = (x, y, z, w)
const vect<3> q_vec({q.x(), q.y(), q.z()});
// t = 2 * cross(q_vec, v)
const vect<3> t = cross(q_vec, v) * 2.0f;
// v' = v + w * t + cross(q_vec, t)
const vect<3> v_prime = v + t * q.w() + cross(q_vec, t);
return v_prime;
}
void ps_move_data::update_orientation(f32 delta_time)
{
if (!delta_time)
return;
// Rotate vector v by quaternion q
const auto rotate_vector = [](const vect<4>& q, const vect<3>& v)
{
const vect<4> qv({0.0f, v.x(), v.y(), v.z()});
const vect<4> q_inv({q.w(), -q.x(), -q.y(), -q.z()});
// t = q * v
vect<4> t;
t.w() = -q.x() * qv.x() - q.y() * qv.y() - q.z() * qv.z();
t.x() = q.w() * qv.x() + q.y() * qv.z() - q.z() * qv.y();
t.y() = q.w() * qv.y() - q.x() * qv.z() + q.z() * qv.x();
t.z() = q.w() * qv.z() + q.x() * qv.y() - q.y() * qv.x();
// r = t * q_inv
vect<4> r;
r.w() = -t.x() * q_inv.x() - t.y() * q_inv.y() - t.z() * q_inv.z();
r.x() = t.w() * q_inv.x() + t.y() * q_inv.z() - t.z() * q_inv.y();
r.y() = t.w() * q_inv.y() - t.x() * q_inv.z() + t.z() * q_inv.x();
r.z() = t.w() * q_inv.z() + t.x() * q_inv.y() - t.y() * q_inv.x();
return vect<3>({r.x(), r.y(), r.z()});
};
if constexpr (use_imu_for_velocity)
{
// Gravity in world frame

View File

@ -15,6 +15,26 @@ struct ps_move_data
template <typename I>
const T& operator[](I i) const { return data[i]; }
vect<Size, T> operator*(f32 s) const
{
vect<Size, T> result = *this;
for (int i = 0; i < Size; ++i)
{
result[i] *= s;
}
return result;
}
vect<Size, T> operator+(const vect<Size, T>& other) const
{
vect<Size, T> result = *this;
for (int i = 0; i < Size; ++i)
{
result[i] += other[i];
}
return result;
}
T x() const requires (Size >= 1) { return data[0]; }
T y() const requires (Size >= 2) { return data[1]; }
T z() const requires (Size >= 3) { return data[2]; }
@ -72,4 +92,7 @@ struct ps_move_data
void reset_sensors();
void update_orientation(f32 delta_time);
void update_velocity(u64 timestamp, be_t<f32> pos_world[4]);
// Rotate vector v by quaternion q
static vect<3> rotate_vector(const vect<4>& q, const vect<3>& v);
};

View File

@ -128,12 +128,6 @@ void fmt_class_string<clan::ClanRequestAction>::format(std::string& out, u64 arg
namespace clan
{
struct curl_memory
{
char* response;
size_t size;
};
size_t clans_client::curl_write_callback(void* data, size_t size, size_t nmemb, void* clientp)
{
const size_t realsize = size * nmemb;
@ -464,7 +458,7 @@ namespace clan
clan.append_child("ticket").text().set(ticket.c_str());
clan.append_child("id").text().set(clan_id);
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
clan.append_child("jid").text().set(jid_str.c_str());
pugi::xml_document response = pugi::xml_document();
@ -656,7 +650,7 @@ namespace clan
clan.append_child("ticket").text().set(ticket.c_str());
clan.append_child("id").text().set(clan_id);
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
clan.append_child("jid").text().set(jid_str.c_str());
pugi::xml_document response = pugi::xml_document();
@ -674,7 +668,7 @@ namespace clan
clan.append_child("ticket").text().set(ticket.c_str());
clan.append_child("id").text().set(clan_id);
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
clan.append_child("jid").text().set(jid_str.c_str());
pugi::xml_document response = pugi::xml_document();
@ -832,7 +826,7 @@ namespace clan
clan.append_child("ticket").text().set(ticket.c_str());
clan.append_child("id").text().set(clan_id);
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
clan.append_child("jid").text().set(jid_str.c_str());
pugi::xml_document response = pugi::xml_document();
@ -850,7 +844,7 @@ namespace clan
clan.append_child("ticket").text().set(ticket.c_str());
clan.append_child("id").text().set(clan_id);
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
clan.append_child("jid").text().set(jid_str.c_str());
pugi::xml_document response = pugi::xml_document();
@ -868,7 +862,7 @@ namespace clan
clan.append_child("ticket").text().set(ticket.c_str());
clan.append_child("id").text().set(clan_id);
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
clan.append_child("jid").text().set(jid_str.c_str());
pugi::xml_document response = pugi::xml_document();
@ -902,7 +896,7 @@ namespace clan
clan.append_child("id").text().set(clan_id);
pugi::xml_node role = clan.append_child("onlinename");
role.text().set(nph.get_npid().handle.data);
role.text().set(np::npid_to_string(nph.get_npid()).c_str());
pugi::xml_node description = clan.append_child("description");
description.text().set(info.description);
@ -990,7 +984,7 @@ namespace clan
clan.append_child("ticket").text().set(ticket.c_str());
clan.append_child("id").text().set(clan_id);
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
clan.append_child("jid").text().set(jid_str.c_str());
pugi::xml_document response = pugi::xml_document();
@ -1008,7 +1002,7 @@ namespace clan
clan.append_child("ticket").text().set(ticket.c_str());
clan.append_child("id").text().set(clan_id);
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
clan.append_child("jid").text().set(jid_str.c_str());
pugi::xml_node role_node = clan.append_child("role");

View File

@ -377,7 +377,7 @@ namespace np
if (!rooms.contains(room_id))
{
np_cache.error("np_cache::get_memberid cache miss room_id: room_id(%d)/npid(%s)", room_id, static_cast<const char*>(npid.handle.data));
np_cache.error("np_cache::get_memberid cache miss room_id: room_id(%d)/npid(%s)", room_id, np::npid_to_string(npid));
return std::nullopt;
}
@ -389,7 +389,7 @@ namespace np
return id;
}
np_cache.error("np_cache::get_memberid cache miss member_id: room_id(%d)/npid(%s)", room_id, static_cast<const char*>(npid.handle.data));
np_cache.error("np_cache::get_memberid cache miss member_id: room_id(%d)/npid(%s)", room_id, np::npid_to_string(npid));
return std::nullopt;
}

Some files were not shown because too many files have changed in this diff Show More