Compare commits
254 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a00f9e4211 | |||
| 3cb9cd7724 | |||
| 9632d0ab8a | |||
| 68b68e676b | |||
| c7ced46707 | |||
| ecc194cb62 | |||
| a1b4ffcf52 | |||
| ae809ad320 | |||
| 7d1c18a569 | |||
| 71f034c4ea | |||
| b156b40f8f | |||
| 8d5a9dc722 | |||
| 06dc6909c4 | |||
| c639b7d5f4 | |||
| a38e144320 | |||
| 059c45f202 | |||
| eccceea7fb | |||
| ad340c3007 | |||
| c214f45e14 | |||
| c8620070b9 | |||
| 61a371b106 | |||
| 5210df6882 | |||
| edb7991979 | |||
| 90d6069a16 | |||
| 880a4e3ca6 | |||
| 18e30c7e44 | |||
| 0113224cb6 | |||
| d1bd936765 | |||
| 2c41eecdb1 | |||
| 1a9e38a1dd | |||
| 9f3f619774 | |||
| 795e234c6f | |||
| 23fdcbfeb8 | |||
| ee3e36672c | |||
| 119b4e4529 | |||
| cc6112cbc4 | |||
| f16c9cd3c6 | |||
| 9e4c24baa3 | |||
| 801609fb78 | |||
| 4c8630d29f | |||
| 4e426af114 | |||
| 5b66fe47db | |||
| 7ae9b15b5c | |||
| 67323994fc | |||
| fcc7a7452a | |||
| 1bb0caed6f | |||
| 23a445619b | |||
| 837d9ca688 | |||
| 9b121a6414 | |||
| 5f1aafb961 | |||
| 615ff08d1a | |||
| 3320d1e1c7 | |||
| 3deb177ab7 | |||
| 41dad58489 | |||
| cb8a240f1a | |||
| 871ef2cdfe | |||
| 6c0bc43776 | |||
| a16d18d83c | |||
| e499c3c6e3 | |||
| ddd261c943 | |||
| a1f9ff0aaa | |||
| d636ea9338 | |||
| 1747e68d1d | |||
| c4c710d39d | |||
| 9a5dbda063 | |||
| bd9c876e36 | |||
| 903d847058 | |||
| 789e42e26f | |||
| 85e11ca341 | |||
| 0da81d22d3 | |||
| 52b993095d | |||
| 363e0a40e5 | |||
| 7ea0a6d642 | |||
| 035d410a89 | |||
| b0b42071f2 | |||
| 5c24009eb5 | |||
| 00340f33c3 | |||
| 01c285fc4a | |||
| 87797e117e | |||
| a5cc9a5517 | |||
| 806acf8cd0 | |||
| 04f6302ecc | |||
| 1df977fae2 | |||
| 9105c2cf4a | |||
| 6d43fcf8fb | |||
| fafae1cbb5 | |||
| 419e673d5b | |||
| 48f47de95c | |||
| 44a1df4353 | |||
| 13b3c78c7f | |||
| e362c2c078 | |||
| 814adbb8c4 | |||
| cf86b6c107 | |||
| 12a83e5a1c | |||
| 0f769eec81 | |||
| 0737c788fc | |||
| 3fe9aea5b5 | |||
| ab6ba848b8 | |||
| 0a38800e44 | |||
| 6be5618d22 | |||
| 95d9462a71 | |||
| e05cd9f47b | |||
| aa09302ca0 | |||
| 6d6b9fd269 | |||
| 6d265452cf | |||
| 65d20f2d08 | |||
| a229e30b08 | |||
| d246a37b11 | |||
| e839595053 | |||
| a711eb31f4 | |||
| b117a6d4cd | |||
| 36b1004e26 | |||
| c75c47fdd2 | |||
| 0f626c8d30 | |||
| 021783dcc1 | |||
| 1f339862db | |||
| 4446d9ce4b | |||
| 92b08a4faf | |||
| d311b4bd53 | |||
| a6dfc3be2f | |||
| 750e7b73e3 | |||
| 6a36967e6f | |||
| d6d7ade6e3 | |||
| 151d98df51 | |||
| fa744e53d3 | |||
| e0df2c584f | |||
| 3c88477270 | |||
| df46e5137c | |||
| ffe8133865 | |||
| 7140e82189 | |||
| cb743da215 | |||
| 4d24ba1f35 | |||
| 87411da95f | |||
| d25d1ecb3a | |||
| 2a00a88e2a | |||
| b01220d2c5 | |||
| 58dd2bff41 | |||
| f34773ed73 | |||
| 9b5cc7cda7 | |||
| 77db8f04b9 | |||
| 9319bc579c | |||
| 73784b9e12 | |||
| 533f960854 | |||
| 765208a181 | |||
| 4417701ea7 | |||
| f66eaf8f44 | |||
| 0dd9c386ee | |||
| af8c871411 | |||
| 50e1e6596e | |||
| d8897c585d | |||
| 7d32dc312f | |||
| eff1e4bd7e | |||
| ac50b46608 | |||
| a0e2a3db1d | |||
| 0cc981ec18 | |||
| 8170b91f76 | |||
| d1a7bb0136 | |||
| 56caa10e56 | |||
| 926496b59d | |||
| 102d30db2d | |||
| 5281a85b67 | |||
| de28c812e8 | |||
| eea73deab3 | |||
| 67c02e3522 | |||
| 19dd2a693b | |||
| 3df133652a | |||
| 80d2034083 | |||
| db9389d24c | |||
| c996be321c | |||
| 6ff6a4989a | |||
| 2655255d4d | |||
| 8485afa3a5 | |||
| 2759091ede | |||
| ae02b71a85 | |||
| 4445569bb0 | |||
| a604394b20 | |||
| dd8a337b14 | |||
| 0572d44996 | |||
| 38aa116c59 | |||
| 61666bae69 | |||
| 362a26a404 | |||
| 81fa3da101 | |||
| bd1fb86492 | |||
| 238298f695 | |||
| 194f7375da | |||
| 3581c5b078 | |||
| c5572acd38 | |||
| acc2fea7e3 | |||
| 3dc7b64fa1 | |||
| 79f2c21dfb | |||
| df36c44bc2 | |||
| c4db65cc08 | |||
| c86ddb3b7d | |||
| a1aebdc5d6 | |||
| b49a1f27eb | |||
| e7fd84e031 | |||
| 9d9e18f614 | |||
| c8199de188 | |||
| fc331da883 | |||
| b6d3fa8c66 | |||
| 3cebea1801 | |||
| 2e9ee81dcd | |||
| cf4da5c4d1 | |||
| 9d1ec0b319 | |||
| 9c5108c1ca | |||
| 2807be7080 | |||
| ec7b18dab5 | |||
| b9e20dc5c9 | |||
| cfdc852f03 | |||
| 92514e33bb | |||
| 572a2a06d1 | |||
| f076fcd539 | |||
| 4ac59875af | |||
| 780ca38f2f | |||
| 97ea5e9dba | |||
| 9e83bc6544 | |||
| 37aefe5811 | |||
| d686b48f65 | |||
| f319362e35 | |||
| 0d9b1654ec | |||
| 940e726754 | |||
| f43824762a | |||
| cd53bb7eff | |||
| 60fc90bb8e | |||
| a9437d69ab | |||
| 5d91caebe9 | |||
| eb4131e1c7 | |||
| 07f0ba213e | |||
| 48382564d1 | |||
| 274386a078 | |||
| 557d015662 | |||
| 5985f0eefa | |||
| 82258915da | |||
| 11a1f090d3 | |||
| a0d48c588a | |||
| 5e2424da58 | |||
| 72a6696f5c | |||
| 9087116ee5 | |||
| 89e0dcba5e | |||
| 7698064f5a | |||
| ab31e96385 | |||
| a9dcad629b | |||
| e49679484a | |||
| 290226539f | |||
| 75efc79930 | |||
| 905496e926 | |||
| 64579eef7e | |||
| e0a648240f | |||
| 7bad8f3348 | |||
| 11a197a387 | |||
| ee1384341e | |||
| fb01ed55e5 | |||
| 5a0a5d9ff1 | |||
| 58e3232710 |
+1
-1
@@ -14,7 +14,7 @@ echo "AVVER=$AVVER" >> ../.ci/ci-vars.env
|
||||
|
||||
cd bin
|
||||
mkdir "rpcs3.app/Contents/lib/"
|
||||
cp "/usr/local/opt/llvm@13/lib/libc++abi.1.0.dylib" "rpcs3.app/Contents/lib/libc++abi.1.dylib"
|
||||
cp "/usr/local/opt/llvm@13/lib/c++/libc++abi.1.0.dylib" "rpcs3.app/Contents/lib/libc++abi.1.dylib"
|
||||
rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtQml.framework" \
|
||||
"rpcs3.app/Contents/Frameworks/QtQmlModels.framework" \
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ Cpp11BracedListStyle: true
|
||||
IndentCaseLabels: false
|
||||
SortIncludes: false
|
||||
ReflowComments: true
|
||||
AlignConsecutiveAssignments: true
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignTrailingComments: true
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
|
||||
Vendored
+1
-1
@@ -4,7 +4,7 @@ include(ExternalProject)
|
||||
|
||||
ExternalProject_Add(moltenvk
|
||||
GIT_REPOSITORY https://github.com/KhronosGroup/MoltenVK.git
|
||||
GIT_TAG 1112005
|
||||
GIT_TAG 260bad4
|
||||
BUILD_IN_SOURCE 1
|
||||
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK
|
||||
CONFIGURE_COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/fetchDependencies" --macos
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/SPIRV/SPIRV-Headers updated: 4995a2f272...b2a156e1c0
Vendored
+1
-1
Submodule 3rdparty/SPIRV/SPIRV-Tools updated: 7826e1941e...5e61ea2098
Vendored
+1
-1
Submodule 3rdparty/flatbuffers updated: 615616cb55...06c5c7ed0b
Vendored
+1
-1
Submodule 3rdparty/glslang/glslang updated: adbf0d3106...10423ec659
Vendored
+1
-1
Submodule 3rdparty/libpng/libpng updated: a40189cf88...0a158f3506
Vendored
+5
-10
@@ -265,9 +265,8 @@ namespace pine
|
||||
case MsgVersion:
|
||||
{
|
||||
char version[256] = {};
|
||||
sprintf(version, "RPCS3 %s", Impl::get_version_and_branch().c_str());
|
||||
snprintf(version, sizeof(version), "RPCS3 %s", Impl::get_version_and_branch().c_str());
|
||||
const u32 size = strlen(version) + 1;
|
||||
version[size] = 0x00;
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
|
||||
goto error;
|
||||
ToArray(ret_buffer, size, ret_cnt);
|
||||
@@ -291,8 +290,7 @@ namespace pine
|
||||
const auto title_string = Impl::get_title();
|
||||
const auto size = title_string.size() + 1;
|
||||
char* title = new char[size];
|
||||
sprintf(title, "%s", title_string.c_str());
|
||||
title[size] = 0x00;
|
||||
snprintf(title, size, "%s", title_string.c_str());
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
|
||||
goto error;
|
||||
ToArray(ret_buffer, size, ret_cnt);
|
||||
@@ -307,8 +305,7 @@ namespace pine
|
||||
const auto title_id_string = Impl::get_title_ID();
|
||||
const auto size = title_id_string.size() + 1;
|
||||
char* title_id = new char[size];
|
||||
sprintf(title_id, "%s", title_id_string.c_str());
|
||||
title_id[size] = 0x00;
|
||||
snprintf(title_id, size, "%s", title_id_string.c_str());
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
|
||||
goto error;
|
||||
ToArray(ret_buffer, size, ret_cnt);
|
||||
@@ -323,8 +320,7 @@ namespace pine
|
||||
const auto hash_string = Impl::get_executable_hash();
|
||||
const auto size = hash_string.size() + 1;
|
||||
char* hash = new char[size];
|
||||
sprintf(hash, "%s", hash_string.c_str());
|
||||
hash[size] = 0x00;
|
||||
snprintf(hash, size, "%s", hash_string.c_str());
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size + 4, buf_size))
|
||||
goto error;
|
||||
ToArray(ret_buffer, size, ret_cnt);
|
||||
@@ -339,8 +335,7 @@ namespace pine
|
||||
const auto game_version_string = Impl::get_app_version();
|
||||
const auto size = game_version_string.size() + 1;
|
||||
char* game_version = new char[size];
|
||||
sprintf(game_version, "%s", game_version_string.c_str());
|
||||
game_version[size] = 0x00;
|
||||
snprintf(game_version, size, "%s", game_version_string.c_str());
|
||||
if (!SafetyChecks(buf_cnt, 0, ret_cnt, size, buf_size))
|
||||
goto error;
|
||||
ToArray(ret_buffer, size, ret_cnt);
|
||||
|
||||
Vendored
+2
-1
@@ -5,9 +5,10 @@ if(USE_SYSTEM_WOLFSSL)
|
||||
target_link_libraries(wolfssl INTERFACE PkgConfig::WolfSSL)
|
||||
else()
|
||||
# TODO(cjj19970505@live.cn)
|
||||
# OPENSSL_EXTRA, WOLFSSL_DES_ECB and HAVE_SNI are unconfigurable from CMake cache.
|
||||
# OPENSSL_EXTRA, and HAVE_SNI are unconfigurable from CMake cache.
|
||||
# but they do have it in a TODO list (wolfssl/CMakeList, 1021)
|
||||
add_compile_definitions(OPENSSL_EXTRA WOLFSSL_DES_ECB HAVE_SNI HAVE_WRITE_DUP)
|
||||
add_compile_definitions(FP_MAX_BITS=8192)
|
||||
|
||||
set(WOLFSSL_TLS13 "no" CACHE INTERNAL "")
|
||||
set(WOLFSSL_SHA224 "yes" CACHE INTERNAL "")
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/zlib/zlib updated: 21767c654d...04f42ceca4
+3
-3
@@ -104,9 +104,9 @@ if(WIN32)
|
||||
add_definitions(-D_WIN32_WINNT=0x0602)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
include_directories(/opt/local/include)
|
||||
link_directories(/opt/local/lib)
|
||||
if(APPLE AND CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
|
||||
include_directories(/opt/homebrew/include)
|
||||
link_directories(/opt/homebrew/lib)
|
||||
endif()
|
||||
|
||||
# Warnings are silenced for 3rdparty code
|
||||
|
||||
@@ -5,10 +5,10 @@ RPCS3
|
||||
[&logo=cirrus-ci)](https://cirrus-ci.com/github/RPCS3/rpcs3)
|
||||
[](https://discord.me/rpcs3)
|
||||
|
||||
The world's first free and open-source PlayStation 3 emulator/debugger, written in C++ for Windows and Linux.
|
||||
The world's first free and open-source PlayStation 3 emulator/debugger, written in C++ for Windows, Linux, macOS and FreeBSD.
|
||||
|
||||
You can find some basic information on our [**website**](https://rpcs3.net/). Game info is being populated on the [**Wiki**](https://wiki.rpcs3.net/).
|
||||
For discussion about this emulator, PS3 emulation, and game compatibility reports, please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.me/RPCS3).
|
||||
For discussion about this emulator, PS3 emulation, and game compatibility reports, please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.gg/RPCS3).
|
||||
|
||||
[**Support Lead Developers Nekotekina and kd-11 on Patreon**](https://www.patreon.com/Nekotekina)
|
||||
|
||||
|
||||
+56
-8
@@ -60,9 +60,15 @@ std::vector<std::string> cfg::make_int_range(s64 min, s64 max)
|
||||
|
||||
bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max)
|
||||
{
|
||||
if (value.empty())
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_uint64(): called with an empty string");
|
||||
return false;
|
||||
}
|
||||
|
||||
s64 result;
|
||||
const char* start = &value.front();
|
||||
const char* end = &value.back() + 1;
|
||||
const char* start = value.data();
|
||||
const char* end = start + value.size();
|
||||
int base = 10;
|
||||
int sign = +1;
|
||||
|
||||
@@ -72,7 +78,7 @@ bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max)
|
||||
start += 1;
|
||||
}
|
||||
|
||||
if (start[0] == '0' && (start[1] == 'x' || start[1] == 'X'))
|
||||
if (start[0] == '0' && value.size() >= 2 && (start[1] == 'x' || start[1] == 'X'))
|
||||
{
|
||||
// Limited hex support
|
||||
base = 16;
|
||||
@@ -106,12 +112,18 @@ std::vector<std::string> cfg::make_uint_range(u64 min, u64 max)
|
||||
|
||||
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max)
|
||||
{
|
||||
if (value.empty())
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_uint64(): called with an empty string");
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 result;
|
||||
const char* start = &value.front();
|
||||
const char* end = &value.back() + 1;
|
||||
const char* start = value.data();
|
||||
const char* end = start + value.size();
|
||||
int base = 10;
|
||||
|
||||
if (start[0] == '0' && (start[1] == 'x' || start[1] == 'X'))
|
||||
if (start[0] == '0' && value.size() >= 2 && (start[1] == 'x' || start[1] == 'X'))
|
||||
{
|
||||
// Limited hex support
|
||||
base = 16;
|
||||
@@ -136,6 +148,42 @@ bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> cfg::make_float_range(f64 min, f64 max)
|
||||
{
|
||||
return {std::to_string(min), std::to_string(max)};
|
||||
}
|
||||
|
||||
bool try_to_float(f64* out, std::string_view value, f64 min, f64 max)
|
||||
{
|
||||
if (value.empty())
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_float(): called with an empty string");
|
||||
return false;
|
||||
}
|
||||
|
||||
// std::from_chars float is yet to be implemented on Xcode it seems
|
||||
// And strtod doesn't support ranged view so we need to ensure it meets a null terminator
|
||||
const std::string str = std::string{value};
|
||||
|
||||
char* end_check{};
|
||||
const double result = std::strtod(str.data(), &end_check);
|
||||
|
||||
if (end_check != str.data() + str.size())
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_float('%s'): invalid float", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result < min || result > max)
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_float('%s'): out of bounds (%f..%f)", value, min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out) *out = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, std::string_view value)
|
||||
{
|
||||
u64 max = umax;
|
||||
@@ -162,8 +210,8 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
|
||||
}
|
||||
|
||||
u64 result;
|
||||
const char* start = &value.front();
|
||||
const char* end = &value.back() + 1;
|
||||
const char* start = value.data();
|
||||
const char* end = start + value.size();
|
||||
int base = 10;
|
||||
|
||||
if (start[0] == '0' && (start[1] == 'x' || start[1] == 'X'))
|
||||
|
||||
@@ -20,6 +20,9 @@ namespace cfg
|
||||
// Format min and max unsigned values
|
||||
std::vector<std::string> make_uint_range(u64 min, u64 max);
|
||||
|
||||
// Format min and max float values
|
||||
std::vector<std::string> make_float_range(f64 min, f64 max);
|
||||
|
||||
// Internal hack
|
||||
bool try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, std::string_view);
|
||||
|
||||
@@ -309,6 +312,72 @@ namespace cfg
|
||||
}
|
||||
};
|
||||
|
||||
// Float entry with custom Min/Max range.
|
||||
template <s32 Min, s32 Max>
|
||||
class _float final : public _base
|
||||
{
|
||||
static_assert(Min < Max, "Invalid cfg::_float range");
|
||||
|
||||
using float_type = f64;
|
||||
atomic_t<float_type> m_value;
|
||||
|
||||
public:
|
||||
float_type def;
|
||||
|
||||
// Expose range
|
||||
static constexpr float_type max = Max;
|
||||
static constexpr float_type min = Min;
|
||||
|
||||
_float(node* owner, const std::string& name, float_type def = std::min<float_type>(Max, std::max<float_type>(Min, 0)), bool dynamic = false)
|
||||
: _base(type::_int, owner, name, dynamic)
|
||||
, m_value(def)
|
||||
, def(def)
|
||||
{
|
||||
}
|
||||
|
||||
operator float_type() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
float_type get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
m_value = def;
|
||||
}
|
||||
|
||||
std::string to_string() const override
|
||||
{
|
||||
return std::to_string(m_value);
|
||||
}
|
||||
|
||||
bool from_string(std::string_view value, bool /*dynamic*/ = false) override
|
||||
{
|
||||
f64 result;
|
||||
if (try_to_float(&result, value, Min, Max))
|
||||
{
|
||||
m_value = static_cast<float_type>(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void set(const f64& value)
|
||||
{
|
||||
m_value = static_cast<float_type>(value);
|
||||
}
|
||||
|
||||
std::vector<std::string> to_list() const override
|
||||
{
|
||||
return make_float_range(Min, Max);
|
||||
}
|
||||
};
|
||||
|
||||
// Alias for 32 bit int
|
||||
using int32 = _int<s32{smin}, s32{smax}>;
|
||||
|
||||
|
||||
+3
-2
@@ -74,7 +74,7 @@ namespace fs
|
||||
|
||||
using enable_bitcopy = std::true_type;
|
||||
|
||||
constexpr bool operator==(const stat_t&) const = default;
|
||||
constexpr bool operator==(const stat_t&) const = default;
|
||||
};
|
||||
|
||||
// Helper, layout is equal to iovec struct
|
||||
@@ -793,7 +793,8 @@ namespace fs
|
||||
|
||||
if (write)
|
||||
{
|
||||
m_stat.mtime = std::max(m_stat.atime, ++m_stat.mtime);
|
||||
m_stat.mtime++;
|
||||
m_stat.mtime = std::max(m_stat.atime, m_stat.mtime);
|
||||
m_stat.ctime = m_stat.mtime;
|
||||
}
|
||||
}
|
||||
|
||||
+143
-83
@@ -20,6 +20,34 @@ LOG_CHANNEL(jit_log, "JIT");
|
||||
|
||||
void jit_announce(uptr func, usz size, std::string_view name)
|
||||
{
|
||||
#ifdef __linux__
|
||||
static const struct tmp_perf_map
|
||||
{
|
||||
std::string name{fmt::format("/tmp/perf-%d.map", getpid())};
|
||||
fs::file data{name, fs::rewrite + fs::append};
|
||||
|
||||
tmp_perf_map() = default;
|
||||
tmp_perf_map(const tmp_perf_map&) = delete;
|
||||
tmp_perf_map& operator=(const tmp_perf_map&) = delete;
|
||||
|
||||
~tmp_perf_map()
|
||||
{
|
||||
fs::remove_file(name);
|
||||
}
|
||||
} s_map;
|
||||
|
||||
if (size && name.size())
|
||||
{
|
||||
s_map.data.write(fmt::format("%x %x %s\n", func, size, name));
|
||||
}
|
||||
|
||||
if (!func && !size && !name.size())
|
||||
{
|
||||
fs::remove_file(s_map.name);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!size)
|
||||
{
|
||||
jit_log.error("Empty function announced: %s (%p)", name, func);
|
||||
@@ -83,12 +111,6 @@ void jit_announce(uptr func, usz size, std::string_view name)
|
||||
dump.write(reinterpret_cast<uchar*>(func), size);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static const fs::file s_map(fmt::format("/tmp/perf-%d.map", getpid()), fs::rewrite + fs::append);
|
||||
|
||||
s_map.write(fmt::format("%x %x %s\n", func, size, name));
|
||||
#endif
|
||||
}
|
||||
|
||||
static u8* get_jit_memory()
|
||||
@@ -357,7 +379,7 @@ asmjit::inline_runtime::~inline_runtime()
|
||||
asmjit::simd_builder::simd_builder(CodeHolder* ch) noexcept
|
||||
: native_asm(ch)
|
||||
{
|
||||
_init(true);
|
||||
_init(0);
|
||||
consts[~v128()] = this->newLabel();
|
||||
}
|
||||
|
||||
@@ -365,9 +387,9 @@ asmjit::simd_builder::~simd_builder()
|
||||
{
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::_init(bool full)
|
||||
void asmjit::simd_builder::_init(uint new_vsize)
|
||||
{
|
||||
if (full && utils::has_avx512_icl())
|
||||
if ((!new_vsize && utils::has_avx512_icl()) || new_vsize == 64)
|
||||
{
|
||||
v0 = x86::zmm0;
|
||||
v1 = x86::zmm1;
|
||||
@@ -377,7 +399,7 @@ void asmjit::simd_builder::_init(bool full)
|
||||
v5 = x86::zmm5;
|
||||
vsize = 64;
|
||||
}
|
||||
else if (full && utils::has_avx2())
|
||||
else if ((!new_vsize && utils::has_avx2()) || new_vsize == 32)
|
||||
{
|
||||
v0 = x86::ymm0;
|
||||
v1 = x86::ymm1;
|
||||
@@ -395,12 +417,13 @@ void asmjit::simd_builder::_init(bool full)
|
||||
v3 = x86::xmm3;
|
||||
v4 = x86::xmm4;
|
||||
v5 = x86::xmm5;
|
||||
vsize = 16;
|
||||
vsize = new_vsize ? new_vsize : 16;
|
||||
}
|
||||
|
||||
if (full && utils::has_avx512())
|
||||
if (utils::has_avx512())
|
||||
{
|
||||
vmask = -1;
|
||||
if (!new_vsize)
|
||||
vmask = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -480,6 +503,10 @@ void asmjit::simd_builder::vec_clobbering_test(u32 esize, const Operand& v, cons
|
||||
{
|
||||
this->emit(x86::Inst::kIdVptest, v, rhs);
|
||||
}
|
||||
else if (esize == 16 && utils::has_avx())
|
||||
{
|
||||
this->emit(x86::Inst::kIdVptest, v, rhs);
|
||||
}
|
||||
else if (esize == 16 && utils::has_sse41())
|
||||
{
|
||||
this->emit(x86::Inst::kIdPtest, v, rhs);
|
||||
@@ -504,6 +531,54 @@ void asmjit::simd_builder::vec_clobbering_test(u32 esize, const Operand& v, cons
|
||||
}
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_broadcast_gpr(u32 esize, const Operand& v, const x86::Gp& r)
|
||||
{
|
||||
if (esize == 2)
|
||||
{
|
||||
if (utils::has_avx512())
|
||||
this->emit(x86::Inst::kIdVpbroadcastw, v, r.r32());
|
||||
else if (utils::has_avx())
|
||||
{
|
||||
this->emit(x86::Inst::kIdVmovd, v, r.r32());
|
||||
if (utils::has_avx2())
|
||||
this->emit(x86::Inst::kIdVpbroadcastw, v, v);
|
||||
else
|
||||
{
|
||||
this->emit(x86::Inst::kIdVpunpcklwd, v, v, v);
|
||||
this->emit(x86::Inst::kIdVpshufd, v, v, Imm(0));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->emit(x86::Inst::kIdMovd, v, r.r32());
|
||||
this->emit(x86::Inst::kIdPunpcklwd, v, v);
|
||||
this->emit(x86::Inst::kIdPshufd, v, v, Imm(0));
|
||||
}
|
||||
}
|
||||
else if (esize == 4)
|
||||
{
|
||||
if (utils::has_avx512())
|
||||
this->emit(x86::Inst::kIdVpbroadcastd, v, r.r32());
|
||||
else if (utils::has_avx())
|
||||
{
|
||||
this->emit(x86::Inst::kIdVmovd, v, r.r32());
|
||||
if (utils::has_avx2())
|
||||
this->emit(x86::Inst::kIdVpbroadcastd, v, v);
|
||||
else
|
||||
this->emit(x86::Inst::kIdVpshufd, v, v, Imm(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->emit(x86::Inst::kIdMovd, v, r.r32());
|
||||
this->emit(x86::Inst::kIdPshufd, v, v, Imm(0));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
}
|
||||
|
||||
asmjit::x86::Mem asmjit::simd_builder::ptr_scale_for_vec(u32 esize, const x86::Gp& base, const x86::Gp& index)
|
||||
{
|
||||
switch (ensure(esize))
|
||||
@@ -530,7 +605,7 @@ void asmjit::simd_builder::vec_load_unaligned(u32 esize, const Operand& v, const
|
||||
this->emit(x86::Inst::kIdVpinsrw, x86::Xmm(v.id()), x86::Xmm(v.id()), src, Imm(0));
|
||||
else if (vsize == 2)
|
||||
this->emit(x86::Inst::kIdPinsrw, v, src, Imm(0));
|
||||
else if (vmask && vmask < 8)
|
||||
else if ((vmask && vmask < 8) || vsize >= 64)
|
||||
this->emit(x86::Inst::kIdVmovdqu16, v, src);
|
||||
else
|
||||
return vec_load_unaligned(vsize, v, src);
|
||||
@@ -542,7 +617,7 @@ void asmjit::simd_builder::vec_load_unaligned(u32 esize, const Operand& v, const
|
||||
this->emit(x86::Inst::kIdVmovd, x86::Xmm(v.id()), src);
|
||||
else if (vsize == 4)
|
||||
this->emit(x86::Inst::kIdMovd, v, src);
|
||||
else if (vmask && vmask < 8)
|
||||
else if ((vmask && vmask < 8) || vsize >= 64)
|
||||
this->emit(x86::Inst::kIdVmovdqu32, v, src);
|
||||
else
|
||||
return vec_load_unaligned(vsize, v, src);
|
||||
@@ -554,7 +629,7 @@ void asmjit::simd_builder::vec_load_unaligned(u32 esize, const Operand& v, const
|
||||
this->emit(x86::Inst::kIdVmovq, x86::Xmm(v.id()), src);
|
||||
else if (vsize == 8)
|
||||
this->emit(x86::Inst::kIdMovq, v, src);
|
||||
else if (vmask && vmask < 8)
|
||||
else if ((vmask && vmask < 8) || vsize >= 64)
|
||||
this->emit(x86::Inst::kIdVmovdqu64, v, src);
|
||||
else
|
||||
return vec_load_unaligned(vsize, v, src);
|
||||
@@ -562,7 +637,9 @@ void asmjit::simd_builder::vec_load_unaligned(u32 esize, const Operand& v, const
|
||||
else if (esize >= 16)
|
||||
{
|
||||
ensure(vsize >= 16);
|
||||
if (utils::has_avx())
|
||||
if ((vmask && vmask < 8) || vsize >= 64)
|
||||
this->emit(x86::Inst::kIdVmovdqu64, v, src); // Not really needed
|
||||
else if (utils::has_avx())
|
||||
this->emit(x86::Inst::kIdVmovdqu, v, src);
|
||||
else
|
||||
this->emit(x86::Inst::kIdMovups, v, src);
|
||||
@@ -636,7 +713,7 @@ void asmjit::simd_builder::_vec_binary_op(x86::Inst::Id sse_op, x86::Inst::Id ve
|
||||
{
|
||||
if (utils::has_avx())
|
||||
{
|
||||
if (vex_op == x86::Inst::kIdNone || this->_extraReg.isReg())
|
||||
if (evex_op != x86::Inst::kIdNone && (vex_op == x86::Inst::kIdNone || this->_extraReg.isReg() || vsize >= 64))
|
||||
{
|
||||
this->evex().emit(evex_op, dst, lhs, rhs);
|
||||
}
|
||||
@@ -694,40 +771,31 @@ void asmjit::simd_builder::vec_umax(u32 esize, const Operand& dst, const Operand
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_umin_horizontal_i128(u32 esize, const x86::Gp& dst, const Operand& src, const Operand& tmp)
|
||||
void asmjit::simd_builder::vec_cmp_eq(u32 esize, const Operand& dst, const Operand& lhs, const Operand& rhs)
|
||||
{
|
||||
using enum x86::Inst::Id;
|
||||
if (!utils::has_sse41())
|
||||
{
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
|
||||
ensure(src != tmp);
|
||||
|
||||
if (esize == 2)
|
||||
{
|
||||
this->emit(utils::has_avx() ? kIdVphminposuw : kIdPhminposuw, x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->emit(utils::has_avx() ? kIdVpextrw : kIdPextrw, dst, x86::Xmm(tmp.id()), Imm(0));
|
||||
}
|
||||
else if (esize == 4)
|
||||
{
|
||||
if (utils::has_avx())
|
||||
if (vsize == 64)
|
||||
{
|
||||
this->vpsrldq(x86::Xmm(tmp.id()), x86::Xmm(src.id()), 8);
|
||||
this->vpminud(x86::Xmm(tmp.id()), x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->vpsrldq(x86::Xmm(src.id()), x86::Xmm(tmp.id()), 4);
|
||||
this->vpminud(x86::Xmm(src.id()), x86::Xmm(src.id()), x86::Xmm(tmp.id()));
|
||||
this->vmovd(dst.r32(), x86::Xmm(src.id()));
|
||||
this->evex().emit(kIdVpcmpeqw, x86::k0, lhs, rhs);
|
||||
this->evex().emit(kIdVpmovm2w, dst, x86::k0);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->movdqa(x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->psrldq(x86::Xmm(tmp.id()), 8);
|
||||
this->pminud(x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->movdqa(x86::Xmm(src.id()), x86::Xmm(tmp.id()));
|
||||
this->psrldq(x86::Xmm(src.id()), 4);
|
||||
this->pminud(x86::Xmm(src.id()), x86::Xmm(tmp.id()));
|
||||
this->movd(dst.r32(), x86::Xmm(src.id()));
|
||||
_vec_binary_op(kIdPcmpeqw, kIdVpcmpeqw, kIdNone, dst, lhs, rhs);
|
||||
}
|
||||
}
|
||||
else if (esize == 4)
|
||||
{
|
||||
if (vsize == 64)
|
||||
{
|
||||
this->evex().emit(kIdVpcmpeqd, x86::k0, lhs, rhs);
|
||||
this->evex().emit(kIdVpmovm2d, dst, x86::k0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_vec_binary_op(kIdPcmpeqw, kIdVpcmpeqw, kIdNone, dst, lhs, rhs);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -736,50 +804,42 @@ void asmjit::simd_builder::vec_umin_horizontal_i128(u32 esize, const x86::Gp& ds
|
||||
}
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_umax_horizontal_i128(u32 esize, const x86::Gp& dst, const Operand& src, const Operand& tmp)
|
||||
void asmjit::simd_builder::vec_extract_high(u32, const Operand& dst, const Operand& src)
|
||||
{
|
||||
using enum x86::Inst::Id;
|
||||
if (!utils::has_sse41())
|
||||
{
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
|
||||
ensure(src != tmp);
|
||||
|
||||
if (esize == 2)
|
||||
{
|
||||
vec_set_all_ones(x86::Xmm(tmp.id()));
|
||||
vec_xor(esize, x86::Xmm(tmp.id()), x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->emit(utils::has_avx() ? kIdVphminposuw : kIdPhminposuw, x86::Xmm(tmp.id()), x86::Xmm(tmp.id()));
|
||||
this->emit(utils::has_avx() ? kIdVpextrw : kIdPextrw, dst, x86::Xmm(tmp.id()), Imm(0));
|
||||
this->not_(dst.r16());
|
||||
}
|
||||
else if (esize == 4)
|
||||
{
|
||||
if (utils::has_avx())
|
||||
{
|
||||
this->vpsrldq(x86::Xmm(tmp.id()), x86::Xmm(src.id()), 8);
|
||||
this->vpmaxud(x86::Xmm(tmp.id()), x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->vpsrldq(x86::Xmm(src.id()), x86::Xmm(tmp.id()), 4);
|
||||
this->vpmaxud(x86::Xmm(src.id()), x86::Xmm(src.id()), x86::Xmm(tmp.id()));
|
||||
this->vmovd(dst.r32(), x86::Xmm(src.id()));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->movdqa(x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->psrldq(x86::Xmm(tmp.id()), 8);
|
||||
this->pmaxud(x86::Xmm(tmp.id()), x86::Xmm(src.id()));
|
||||
this->movdqa(x86::Xmm(src.id()), x86::Xmm(tmp.id()));
|
||||
this->psrldq(x86::Xmm(src.id()), 4);
|
||||
this->pmaxud(x86::Xmm(src.id()), x86::Xmm(tmp.id()));
|
||||
this->movd(dst.r32(), x86::Xmm(src.id()));
|
||||
}
|
||||
}
|
||||
if (vsize == 32)
|
||||
this->vextracti32x8(x86::Ymm(dst.id()), x86::Zmm(src.id()), 1);
|
||||
else if (vsize == 16)
|
||||
this->vextracti128(x86::Xmm(dst.id()), x86::Ymm(src.id()), 1);
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Unimplemented");
|
||||
if (utils::has_avx())
|
||||
this->vpsrldq(x86::Xmm(dst.id()), x86::Xmm(src.id()), vsize);
|
||||
else
|
||||
{
|
||||
this->movdqa(x86::Xmm(dst.id()), x86::Xmm(src.id()));
|
||||
this->psrldq(x86::Xmm(dst.id()), vsize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void asmjit::simd_builder::vec_extract_gpr(u32 esize, const x86::Gp& dst, const Operand& src)
|
||||
{
|
||||
if (esize == 8 && utils::has_avx())
|
||||
this->vmovq(dst.r64(), x86::Xmm(src.id()));
|
||||
else if (esize == 8)
|
||||
this->movq(dst.r64(), x86::Xmm(src.id()));
|
||||
else if (esize == 4 && utils::has_avx())
|
||||
this->vmovd(dst.r32(), x86::Xmm(src.id()));
|
||||
else if (esize == 4)
|
||||
this->movd(dst.r32(), x86::Xmm(src.id()));
|
||||
else if (esize == 2 && utils::has_avx())
|
||||
this->vpextrw(dst.r32(), x86::Xmm(src.id()), 0);
|
||||
else if (esize == 2)
|
||||
this->pextrw(dst.r32(), x86::Xmm(src.id()), 0);
|
||||
else
|
||||
fmt::throw_exception("Unimplemented");
|
||||
}
|
||||
|
||||
#endif /* X86 */
|
||||
|
||||
#ifdef LLVM_AVAILABLE
|
||||
|
||||
+74
-28
@@ -226,12 +226,13 @@ namespace asmjit
|
||||
|
||||
void operator()() noexcept;
|
||||
|
||||
void _init(bool full);
|
||||
void _init(uint new_vsize = 0);
|
||||
void vec_cleanup_ret();
|
||||
void vec_set_all_zeros(const Operand& v);
|
||||
void vec_set_all_ones(const Operand& v);
|
||||
void vec_set_const(const Operand& v, const v128& value);
|
||||
void vec_clobbering_test(u32 esize, const Operand& v, const Operand& rhs);
|
||||
void vec_broadcast_gpr(u32 esize, const Operand& v, const x86::Gp& r);
|
||||
|
||||
// return x86::ptr(base, ctr, X, 0) where X is set for esize accordingly
|
||||
x86::Mem ptr_scale_for_vec(u32 esize, const x86::Gp& base, const x86::Gp& index);
|
||||
@@ -260,11 +261,18 @@ namespace asmjit
|
||||
_vec_binary_op(kIdPor, kIdVpor, kIdVpord, dst, lhs, rhs);
|
||||
}
|
||||
|
||||
void vec_andn(u32, const Operand& dst, const Operand& lhs, const Operand& rhs)
|
||||
{
|
||||
using enum x86::Inst::Id;
|
||||
_vec_binary_op(kIdPandn, kIdVpandn, kIdVpandnd, dst, lhs, rhs);
|
||||
}
|
||||
|
||||
void vec_umin(u32 esize, const Operand& dst, const Operand& lhs, const Operand& rhs);
|
||||
void vec_umax(u32 esize, const Operand& dst, const Operand& lhs, const Operand& rhs);
|
||||
void vec_cmp_eq(u32 esize, const Operand& dst, const Operand& lhs, const Operand& rhs);
|
||||
|
||||
void vec_umin_horizontal_i128(u32 esize, const x86::Gp& dst, const Operand& src, const Operand& tmp);
|
||||
void vec_umax_horizontal_i128(u32 esize, const x86::Gp& dst, const Operand& src, const Operand& tmp);
|
||||
void vec_extract_high(u32 esize, const Operand& dst, const Operand& src);
|
||||
void vec_extract_gpr(u32 esize, const x86::Gp& dst, const Operand& src);
|
||||
|
||||
simd_builder& keep_if_not_masked()
|
||||
{
|
||||
@@ -287,7 +295,7 @@ namespace asmjit
|
||||
return *this;
|
||||
}
|
||||
|
||||
void build_loop(u32 esize, auto reg_ctr, auto reg_cnt, auto&& build, auto&& reduce)
|
||||
void build_loop(u32 esize, const x86::Gp& reg_ctr, const x86::Gp& reg_cnt, auto&& build, auto&& reduce)
|
||||
{
|
||||
ensure((esize & (esize - 1)) == 0);
|
||||
ensure(esize <= vsize);
|
||||
@@ -299,47 +307,85 @@ namespace asmjit
|
||||
const u32 step = vsize / esize;
|
||||
|
||||
this->xor_(reg_ctr.r32(), reg_ctr.r32()); // Reset counter reg
|
||||
this->sub(reg_cnt, step);
|
||||
this->cmp(reg_cnt, step);
|
||||
this->jb(next); // If count < step, skip main loop body
|
||||
this->align(AlignMode::kCode, 16);
|
||||
this->bind(body);
|
||||
this->sub(reg_cnt, step);
|
||||
build();
|
||||
this->add(reg_ctr, step);
|
||||
this->sub(reg_cnt, step);
|
||||
this->ja(body);
|
||||
this->cmp(reg_cnt, step);
|
||||
this->jae(body);
|
||||
this->bind(next);
|
||||
if (!vmask)
|
||||
reduce();
|
||||
this->add(reg_cnt, step);
|
||||
this->jz(exit);
|
||||
|
||||
if (vmask)
|
||||
{
|
||||
// Build single last iteration (masked)
|
||||
this->bzhi(reg_cnt, x86::Mem(consts[~u128()], 0), reg_cnt);
|
||||
this->kmovq(x86::k7, reg_cnt);
|
||||
this->test(reg_cnt, reg_cnt);
|
||||
this->jz(exit);
|
||||
|
||||
if (esize == 1 && vsize == 64)
|
||||
{
|
||||
this->bzhi(reg_cnt.r64(), x86::Mem(consts[~u128()], 0), reg_cnt.r64());
|
||||
this->kmovq(x86::k7, reg_cnt.r64());
|
||||
}
|
||||
else
|
||||
{
|
||||
this->bzhi(reg_cnt.r32(), x86::Mem(consts[~u128()], 0), reg_cnt.r32());
|
||||
this->kmovd(x86::k7, reg_cnt.r32());
|
||||
}
|
||||
|
||||
vmask = 7;
|
||||
build();
|
||||
vmask = -1;
|
||||
reduce();
|
||||
|
||||
// Rollout reduction step
|
||||
this->bind(exit);
|
||||
while (true)
|
||||
{
|
||||
vsize /= 2;
|
||||
if (vsize < esize)
|
||||
break;
|
||||
this->_init(vsize);
|
||||
reduce();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Build tail loop (reduced vector width)
|
||||
Label body = this->newLabel();
|
||||
this->align(AlignMode::kCode, 16);
|
||||
this->bind(body);
|
||||
const uint vsz = vsize / step;
|
||||
this->_init(false);
|
||||
vsize = vsz;
|
||||
build();
|
||||
this->_init(true);
|
||||
this->inc(reg_ctr);
|
||||
this->sub(reg_cnt, 1);
|
||||
this->ja(body);
|
||||
// Build unrolled loop tail (reduced vector width)
|
||||
while (true)
|
||||
{
|
||||
vsize /= 2;
|
||||
if (vsize < esize)
|
||||
break;
|
||||
|
||||
// Shall not clobber flags
|
||||
this->_init(vsize);
|
||||
reduce();
|
||||
|
||||
if (vsize == esize)
|
||||
{
|
||||
// Last "iteration"
|
||||
this->test(reg_cnt, reg_cnt);
|
||||
this->jz(exit);
|
||||
build();
|
||||
}
|
||||
else
|
||||
{
|
||||
const u32 step = vsize / esize;
|
||||
Label next = this->newLabel();
|
||||
this->cmp(reg_cnt, step);
|
||||
this->jb(next);
|
||||
build();
|
||||
this->add(reg_ctr, step);
|
||||
this->sub(reg_cnt, step);
|
||||
this->bind(next);
|
||||
}
|
||||
}
|
||||
|
||||
this->bind(exit);
|
||||
}
|
||||
|
||||
this->bind(exit);
|
||||
this->_init(0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ bool try_to_int64(s64* out, std::string_view value, s64 min, s64 max);
|
||||
// Convert string to unsigned integer
|
||||
bool try_to_uint64(u64* out, std::string_view value, u64 min, u64 max);
|
||||
|
||||
// Convert string to float
|
||||
bool try_to_float(f64* out, std::string_view value, f64 min, f64 max);
|
||||
|
||||
// Get the file extension of a file path ("png", "jpg", etc.)
|
||||
std::string get_file_extension(const std::string& file_path);
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
#include <process.h>
|
||||
#include <sysinfoapi.h>
|
||||
#else
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#define _XOPEN_SOURCE
|
||||
#define __USE_GNU
|
||||
|
||||
@@ -939,7 +939,7 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
|
||||
}
|
||||
|
||||
std::basic_string<u32> applied_total;
|
||||
const auto& container = m_map.at(name);
|
||||
const auto& container = ::at32(m_map, name);
|
||||
const auto& serial = Emu.GetTitleID();
|
||||
const auto& app_version = Emu.GetAppVersion();
|
||||
|
||||
@@ -975,7 +975,7 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& app_versions = serials.at(found_serial);
|
||||
const auto& app_versions = ::at32(serials, found_serial);
|
||||
std::string found_app_version;
|
||||
|
||||
if (app_versions.find(app_version) != app_versions.end())
|
||||
@@ -988,7 +988,7 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
|
||||
is_all_versions = true;
|
||||
}
|
||||
|
||||
if (!found_app_version.empty() && app_versions.at(found_app_version))
|
||||
if (!found_app_version.empty() && ::at32(app_versions, found_app_version))
|
||||
{
|
||||
// This patch is enabled
|
||||
if (is_all_serials)
|
||||
@@ -1073,7 +1073,7 @@ void patch_engine::unload(const std::string& name)
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& container = m_map.at(name);
|
||||
const auto& container = ::at32(m_map, name);
|
||||
|
||||
for (const auto& [description, patch] : container.patch_info_map)
|
||||
{
|
||||
|
||||
@@ -30,7 +30,8 @@ namespace utils
|
||||
|
||||
if (lo())
|
||||
{
|
||||
version += "." + std::to_string(lo());
|
||||
version += '.';
|
||||
version += std::to_string(lo());
|
||||
}
|
||||
|
||||
if (type() != version_type::release)
|
||||
@@ -40,7 +41,8 @@ namespace utils
|
||||
version += "-" + postfix();
|
||||
}
|
||||
|
||||
version += " " + utils::to_string(type());
|
||||
version += ' ';
|
||||
version += utils::to_string(type());
|
||||
|
||||
if (type_index() > 1)
|
||||
{
|
||||
|
||||
+1
-1
Submodule llvm updated: 5521155be5...9b52b6c39a
@@ -111,7 +111,7 @@ std::pair<AudioChannelCnt, AudioChannelCnt> AudioBackend::get_channel_count_and_
|
||||
audio_out_configuration& audio_out_cfg = g_fxo->get<audio_out_configuration>();
|
||||
std::lock_guard lock(audio_out_cfg.mtx);
|
||||
ensure(device_index < audio_out_cfg.out.size());
|
||||
const audio_out_configuration::audio_out& out = audio_out_cfg.out.at(device_index);
|
||||
const audio_out_configuration::audio_out& out = ::at32(audio_out_cfg.out, device_index);
|
||||
return out.get_channel_count_and_downmixer();
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ AudioChannelCnt AudioBackend::get_max_channel_count(u32 device_index)
|
||||
audio_out_configuration& audio_out_cfg = g_fxo->get<audio_out_configuration>();
|
||||
std::lock_guard lock(audio_out_cfg.mtx);
|
||||
ensure(device_index < audio_out_cfg.out.size());
|
||||
const audio_out_configuration::audio_out& out = audio_out_cfg.out.at(device_index);
|
||||
const audio_out_configuration::audio_out& out = ::at32(audio_out_cfg.out, device_index);
|
||||
|
||||
AudioChannelCnt count = AudioChannelCnt::STEREO;
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ enum class AudioChannelCnt : u32
|
||||
enum class AudioStateEvent : u32
|
||||
{
|
||||
UNSPECIFIED_ERROR,
|
||||
DEFAULT_DEVICE_CHANGED,
|
||||
DEFAULT_DEVICE_MAYBE_CHANGED,
|
||||
};
|
||||
|
||||
class AudioBackend
|
||||
@@ -226,7 +226,7 @@ protected:
|
||||
AudioFreq m_sampling_rate = AudioFreq::FREQ_48K;
|
||||
AudioChannelCnt m_channels = AudioChannelCnt::STEREO;
|
||||
|
||||
shared_mutex m_cb_mutex{};
|
||||
std::timed_mutex m_cb_mutex{};
|
||||
std::function<u32(u32, void *)> m_write_callback{};
|
||||
|
||||
shared_mutex m_state_cb_mutex{};
|
||||
|
||||
@@ -22,14 +22,14 @@ CubebBackend::CubebBackend()
|
||||
}
|
||||
#endif
|
||||
|
||||
if (int err = cubeb_init(&m_ctx, "RPCS3", nullptr))
|
||||
cubeb *ctx{};
|
||||
if (int err = cubeb_init(&ctx, "RPCS3", nullptr))
|
||||
{
|
||||
Cubeb.error("cubeb_init() failed: %i", err);
|
||||
m_ctx = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (int err = cubeb_register_device_collection_changed(m_ctx, CUBEB_DEVICE_TYPE_OUTPUT, device_collection_changed_cb, this))
|
||||
if (int err = cubeb_register_device_collection_changed(ctx, CUBEB_DEVICE_TYPE_OUTPUT, device_collection_changed_cb, this))
|
||||
{
|
||||
Cubeb.error("cubeb_register_device_collection_changed() failed: %i", err);
|
||||
}
|
||||
@@ -38,7 +38,10 @@ CubebBackend::CubebBackend()
|
||||
m_dev_collection_cb_enabled = true;
|
||||
}
|
||||
|
||||
Cubeb.notice("Using backend %s", cubeb_get_backend_id(m_ctx));
|
||||
Cubeb.notice("Using backend %s", cubeb_get_backend_id(ctx));
|
||||
|
||||
std::lock_guard cb_lock{m_state_cb_mutex};
|
||||
m_ctx = ctx;
|
||||
}
|
||||
|
||||
CubebBackend::~CubebBackend()
|
||||
@@ -78,8 +81,19 @@ bool CubebBackend::Operational()
|
||||
|
||||
bool CubebBackend::DefaultDeviceChanged()
|
||||
{
|
||||
std::lock_guard lock{m_dev_sw_mutex};
|
||||
return !m_reset_req.observe() && m_default_dev_changed;
|
||||
if (m_default_device.empty() || m_reset_req.observe())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
device_handle device = GetDevice();
|
||||
if (!device.handle)
|
||||
{
|
||||
Cubeb.error("Selected device not found. Trying alternative approach...");
|
||||
device = GetDefaultDeviceAlt(m_sampling_rate, m_sample_size, m_channels);
|
||||
}
|
||||
|
||||
return !device.handle || device.id != m_default_device;
|
||||
}
|
||||
|
||||
bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
@@ -90,20 +104,23 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
std::lock_guard dev_sw_lock{m_dev_sw_mutex};
|
||||
CloseUnlocked();
|
||||
Close();
|
||||
std::lock_guard lock{m_cb_mutex};
|
||||
|
||||
const bool use_default_device = dev_id.empty() || dev_id == audio_device_enumerator::DEFAULT_DEV_ID;
|
||||
auto [dev_handle, dev_ident, dev_ch_cnt] = GetDevice(use_default_device ? "" : dev_id);
|
||||
|
||||
if (!dev_handle)
|
||||
if (use_default_device) Cubeb.notice("Trying to open default device");
|
||||
else Cubeb.notice("Trying to open device with dev_id='%s'", dev_id);
|
||||
|
||||
device_handle device = GetDevice(use_default_device ? "" : dev_id);
|
||||
|
||||
if (!device.handle)
|
||||
{
|
||||
if (use_default_device)
|
||||
{
|
||||
std::tie(dev_handle, dev_ident, dev_ch_cnt) = GetDefaultDeviceAlt(freq, sample_size, ch_cnt);
|
||||
device = GetDefaultDeviceAlt(freq, sample_size, ch_cnt);
|
||||
|
||||
if (!dev_handle)
|
||||
if (!device.handle)
|
||||
{
|
||||
Cubeb.error("Cannot detect default device. Channel count detection unavailable.");
|
||||
}
|
||||
@@ -115,15 +132,21 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
||||
}
|
||||
}
|
||||
|
||||
if (dev_ch_cnt == 0)
|
||||
if (device.ch_cnt == 0)
|
||||
{
|
||||
Cubeb.error("Device reported invalid channel count, using stereo instead");
|
||||
dev_ch_cnt = 2;
|
||||
device.ch_cnt = 2;
|
||||
}
|
||||
|
||||
if (use_default_device)
|
||||
{
|
||||
std::lock_guard lock{m_state_cb_mutex};
|
||||
m_default_device = device.id;
|
||||
}
|
||||
|
||||
m_sampling_rate = freq;
|
||||
m_sample_size = sample_size;
|
||||
m_channels = static_cast<AudioChannelCnt>(std::min(static_cast<u32>(convert_channel_count(dev_ch_cnt)), static_cast<u32>(ch_cnt)));
|
||||
m_channels = static_cast<AudioChannelCnt>(std::min(static_cast<u32>(convert_channel_count(device.ch_cnt)), static_cast<u32>(ch_cnt)));
|
||||
full_sample_size = get_channels() * get_sample_size();
|
||||
|
||||
cubeb_stream_params stream_param{};
|
||||
@@ -141,7 +164,7 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
||||
fmt::throw_exception("Invalid audio channel count");
|
||||
}
|
||||
}();
|
||||
stream_param.prefs = m_dev_collection_cb_enabled && dev_handle ? CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING : CUBEB_STREAM_PREF_NONE;
|
||||
stream_param.prefs = m_dev_collection_cb_enabled && device.handle ? CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING : CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
u32 min_latency{};
|
||||
if (int err = cubeb_get_min_latency(m_ctx, &stream_param, &min_latency))
|
||||
@@ -152,7 +175,7 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
||||
|
||||
const u32 stream_latency = std::max(static_cast<u32>(AUDIO_MIN_LATENCY * get_sampling_rate()), min_latency);
|
||||
|
||||
if (int err = cubeb_stream_init(m_ctx, &m_stream, "Main stream", nullptr, nullptr, dev_handle, &stream_param, stream_latency, data_cb, state_cb, this))
|
||||
if (int err = cubeb_stream_init(m_ctx, &m_stream, "Main stream", nullptr, nullptr, device.handle, &stream_param, stream_latency, data_cb, state_cb, this))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_init() failed: %i", err);
|
||||
m_stream = nullptr;
|
||||
@@ -162,7 +185,7 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
||||
if (int err = cubeb_stream_start(m_stream))
|
||||
{
|
||||
Cubeb.error("cubeb_stream_start() failed: %i", err);
|
||||
CloseUnlocked();
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -171,15 +194,10 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
||||
Cubeb.error("cubeb_stream_set_volume() failed: %i", err);
|
||||
}
|
||||
|
||||
if (use_default_device)
|
||||
{
|
||||
m_current_device = dev_ident;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CubebBackend::CloseUnlocked()
|
||||
void CubebBackend::Close()
|
||||
{
|
||||
if (m_stream != nullptr)
|
||||
{
|
||||
@@ -189,21 +207,17 @@ void CubebBackend::CloseUnlocked()
|
||||
}
|
||||
|
||||
cubeb_stream_destroy(m_stream);
|
||||
m_stream = nullptr;
|
||||
}
|
||||
|
||||
m_playing = false;
|
||||
m_last_sample.fill(0);
|
||||
{
|
||||
std::lock_guard lock{m_cb_mutex};
|
||||
m_stream = nullptr;
|
||||
m_playing = false;
|
||||
m_last_sample.fill(0);
|
||||
}
|
||||
|
||||
m_default_dev_changed = false;
|
||||
m_current_device.clear();
|
||||
}
|
||||
|
||||
void CubebBackend::Close()
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
std::lock_guard dev_sw_lock{m_dev_sw_mutex};
|
||||
CloseUnlocked();
|
||||
std::lock_guard lock{m_state_cb_mutex};
|
||||
m_default_device.clear();
|
||||
}
|
||||
|
||||
void CubebBackend::Play()
|
||||
@@ -253,10 +267,13 @@ f64 CubebBackend::GetCallbackFrameLen()
|
||||
return std::max<f64>(AUDIO_MIN_LATENCY, static_cast<f64>(stream_latency) / get_sampling_rate());
|
||||
}
|
||||
|
||||
std::tuple<cubeb_devid, std::string, u32> CubebBackend::GetDevice(std::string_view dev_id)
|
||||
CubebBackend::device_handle CubebBackend::GetDevice(std::string_view dev_id)
|
||||
{
|
||||
const bool default_dev = dev_id.empty();
|
||||
|
||||
if (default_dev) Cubeb.notice("Searching for default device");
|
||||
else Cubeb.notice("Searching for device with dev_id='%s'", dev_id);
|
||||
|
||||
cubeb_device_collection dev_collection{};
|
||||
if (int err = cubeb_enumerate_devices(m_ctx, CUBEB_DEVICE_TYPE_OUTPUT, &dev_collection))
|
||||
{
|
||||
@@ -274,14 +291,24 @@ std::tuple<cubeb_devid, std::string, u32> CubebBackend::GetDevice(std::string_vi
|
||||
return {};
|
||||
}
|
||||
|
||||
std::tuple<cubeb_devid, std::string, u32> result{};
|
||||
Cubeb.notice("Found %d possible output devices", dev_collection.count);
|
||||
|
||||
device_handle result{};
|
||||
|
||||
for (u64 dev_idx = 0; dev_idx < dev_collection.count; dev_idx++)
|
||||
{
|
||||
const cubeb_device_info& dev_info = dev_collection.device[dev_idx];
|
||||
const std::string dev_ident{dev_info.device_id};
|
||||
|
||||
if (dev_ident.empty())
|
||||
device_handle device{};
|
||||
device.handle = dev_info.devid;
|
||||
device.ch_cnt = dev_info.max_channels;
|
||||
|
||||
if (dev_info.device_id)
|
||||
{
|
||||
device.id = dev_info.device_id;
|
||||
}
|
||||
|
||||
if (device.id.empty())
|
||||
{
|
||||
Cubeb.error("device_id is missing from device");
|
||||
continue;
|
||||
@@ -291,17 +318,26 @@ std::tuple<cubeb_devid, std::string, u32> CubebBackend::GetDevice(std::string_vi
|
||||
{
|
||||
if (dev_info.preferred & CUBEB_DEVICE_PREF_MULTIMEDIA)
|
||||
{
|
||||
result = {dev_info.devid, dev_ident, dev_info.max_channels};
|
||||
result = std::move(device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (dev_ident == dev_id)
|
||||
else if (device.id == dev_id)
|
||||
{
|
||||
result = {dev_info.devid, dev_ident, dev_info.max_channels};
|
||||
result = std::move(device);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.handle)
|
||||
{
|
||||
Cubeb.notice("Found device '%s' with %d channels", result.id, result.ch_cnt);
|
||||
}
|
||||
else
|
||||
{
|
||||
Cubeb.notice("No device found for dev_id='%s'", dev_id);
|
||||
}
|
||||
|
||||
if (int err = cubeb_device_collection_destroy(m_ctx, &dev_collection))
|
||||
{
|
||||
Cubeb.error("cubeb_device_collection_destroy() failed: %i", err);
|
||||
@@ -310,8 +346,10 @@ std::tuple<cubeb_devid, std::string, u32> CubebBackend::GetDevice(std::string_vi
|
||||
return result;
|
||||
};
|
||||
|
||||
std::tuple<cubeb_devid, std::string, u32> CubebBackend::GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
CubebBackend::device_handle CubebBackend::GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
{
|
||||
Cubeb.notice("Starting alternative search for default device with freq=%d, sample_size=%d and ch_cnt=%d", static_cast<u32>(freq), static_cast<u32>(sample_size), static_cast<u32>(ch_cnt));
|
||||
|
||||
cubeb_stream_params param =
|
||||
{
|
||||
.format = sample_size == AudioSampleSize::S16 ? CUBEB_SAMPLE_S16NE : CUBEB_SAMPLE_FLOAT32NE,
|
||||
@@ -340,14 +378,19 @@ std::tuple<cubeb_devid, std::string, u32> CubebBackend::GetDefaultDeviceAlt(Audi
|
||||
|
||||
cubeb_device* crnt_dev{};
|
||||
|
||||
if (int err = cubeb_stream_get_current_device(tmp_stream, &crnt_dev))
|
||||
if (int err = cubeb_stream_get_current_device(tmp_stream, &crnt_dev); err != CUBEB_OK || !crnt_dev)
|
||||
{
|
||||
Cubeb.error("cubeb_stream_get_current_device() failed: %i", err);
|
||||
Cubeb.error("cubeb_stream_get_current_device() failed: err=%i, crnt_dev=%d", err, !!crnt_dev);
|
||||
cubeb_stream_destroy(tmp_stream);
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::string out_dev_name{crnt_dev->output_name};
|
||||
std::string out_dev_name;
|
||||
|
||||
if (crnt_dev->output_name)
|
||||
{
|
||||
out_dev_name = crnt_dev->output_name;
|
||||
}
|
||||
|
||||
if (int err = cubeb_stream_device_destroy(tmp_stream, crnt_dev))
|
||||
{
|
||||
@@ -358,19 +401,46 @@ std::tuple<cubeb_devid, std::string, u32> CubebBackend::GetDefaultDeviceAlt(Audi
|
||||
|
||||
if (out_dev_name.empty())
|
||||
{
|
||||
Cubeb.notice("No default device available");
|
||||
return {};
|
||||
}
|
||||
|
||||
return GetDevice(out_dev_name);
|
||||
}
|
||||
|
||||
long CubebBackend::data_cb(cubeb_stream* /* stream */, void* user_ptr, void const* /* input_buffer */, void* output_buffer, long nframes)
|
||||
long CubebBackend::data_cb(cubeb_stream* stream, void* user_ptr, void const* /* input_buffer */, void* output_buffer, long nframes)
|
||||
{
|
||||
if (nframes <= 0)
|
||||
{
|
||||
Cubeb.error("data_cb called with nframes=%d", nframes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!output_buffer)
|
||||
{
|
||||
Cubeb.error("data_cb called with invalid output_buffer");
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
if (!stream)
|
||||
{
|
||||
Cubeb.error("data_cb called with invalid stream");
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr);
|
||||
ensure(cubeb);
|
||||
|
||||
std::unique_lock lock(cubeb->m_cb_mutex, std::defer_lock);
|
||||
|
||||
if (nframes && !cubeb->m_reset_req.observe() && lock.try_lock() && cubeb->m_write_callback && cubeb->m_playing)
|
||||
if (!cubeb->m_reset_req.observe() && lock.try_lock_for(std::chrono::microseconds{50}) && cubeb->m_write_callback && cubeb->m_playing)
|
||||
{
|
||||
if (stream != cubeb->m_stream)
|
||||
{
|
||||
// Cubeb.error("data_cb called with unknown stream");
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
const u32 sample_size = cubeb->full_sample_size.observe();
|
||||
const u32 bytes_req = nframes * sample_size;
|
||||
u32 written = std::min(cubeb->m_write_callback(bytes_req, output_buffer), bytes_req);
|
||||
@@ -390,65 +460,102 @@ long CubebBackend::data_cb(cubeb_stream* /* stream */, void* user_ptr, void cons
|
||||
{
|
||||
// Stream parameters are modified only after stream_destroy. stream_destroy will return
|
||||
// only after this callback returns, so it's safe to access full_sample_size here.
|
||||
memset(output_buffer, 0, nframes * cubeb->full_sample_size.observe());
|
||||
memset(output_buffer, 0, nframes * cubeb->full_sample_size);
|
||||
}
|
||||
|
||||
return nframes;
|
||||
}
|
||||
|
||||
void CubebBackend::state_cb(cubeb_stream* /* stream */, void* user_ptr, cubeb_state state)
|
||||
void CubebBackend::state_cb(cubeb_stream* stream, void* user_ptr, cubeb_state state)
|
||||
{
|
||||
CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr);
|
||||
if (!stream)
|
||||
{
|
||||
Cubeb.error("state_cb called with invalid stream");
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == CUBEB_STATE_ERROR)
|
||||
CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr);
|
||||
ensure(cubeb);
|
||||
|
||||
std::lock_guard lock(cubeb->m_state_cb_mutex);
|
||||
|
||||
if (stream != cubeb->m_stream)
|
||||
{
|
||||
Cubeb.error("state_cb called with unknown stream");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case CUBEB_STATE_ERROR:
|
||||
{
|
||||
Cubeb.error("Stream entered error state");
|
||||
|
||||
std::lock_guard lock(cubeb->m_state_cb_mutex);
|
||||
|
||||
if (!cubeb->m_reset_req.test_and_set() && cubeb->m_state_callback)
|
||||
{
|
||||
cubeb->m_state_callback(AudioStateEvent::UNSPECIFIED_ERROR);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case CUBEB_STATE_STARTED:
|
||||
{
|
||||
Cubeb.notice("Stream started");
|
||||
break;
|
||||
}
|
||||
case CUBEB_STATE_STOPPED:
|
||||
{
|
||||
Cubeb.notice("Stream stopped");
|
||||
break;
|
||||
}
|
||||
case CUBEB_STATE_DRAINED:
|
||||
{
|
||||
Cubeb.notice("Stream drained");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Cubeb.notice("Stream entered unknown state %d", static_cast<u32>(state));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CubebBackend::device_collection_changed_cb(cubeb* /* context */, void* user_ptr)
|
||||
void CubebBackend::device_collection_changed_cb(cubeb* context, void* user_ptr)
|
||||
{
|
||||
CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr);
|
||||
|
||||
Cubeb.notice("Device collection changed");
|
||||
std::lock_guard lock{cubeb->m_dev_sw_mutex};
|
||||
|
||||
// Non default device is used (or default device cannot be detected)
|
||||
if (cubeb->m_current_device.empty())
|
||||
if (!context)
|
||||
{
|
||||
Cubeb.error("device_collection_changed_cb called with invalid stream");
|
||||
return;
|
||||
}
|
||||
|
||||
CubebBackend* const cubeb = static_cast<CubebBackend*>(user_ptr);
|
||||
ensure(cubeb);
|
||||
|
||||
if (cubeb->m_reset_req.observe())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto [handle, dev_id, ch_cnt] = cubeb->GetDevice();
|
||||
if (!handle)
|
||||
{
|
||||
std::tie(handle, dev_id, ch_cnt) = cubeb->GetDefaultDeviceAlt(cubeb->m_sampling_rate, cubeb->m_sample_size, cubeb->m_channels);
|
||||
}
|
||||
|
||||
std::lock_guard cb_lock{cubeb->m_state_cb_mutex};
|
||||
|
||||
if (!handle)
|
||||
if (context != cubeb->m_ctx)
|
||||
{
|
||||
// No devices available
|
||||
if (!cubeb->m_reset_req.test_and_set() && cubeb->m_state_callback)
|
||||
{
|
||||
cubeb->m_state_callback(AudioStateEvent::UNSPECIFIED_ERROR);
|
||||
}
|
||||
Cubeb.error("device_collection_changed_cb called with unkown context");
|
||||
return;
|
||||
}
|
||||
else if (!cubeb->m_reset_req.observe() && dev_id != cubeb->m_current_device)
|
||||
{
|
||||
cubeb->m_default_dev_changed = true;
|
||||
|
||||
if (cubeb->m_state_callback)
|
||||
{
|
||||
cubeb->m_state_callback(AudioStateEvent::DEFAULT_DEVICE_CHANGED);
|
||||
}
|
||||
// Non default device is used (or default device cannot be detected)
|
||||
if (cubeb->m_default_device.empty())
|
||||
{
|
||||
Cubeb.notice("Skipping default device notification");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cubeb->m_state_callback)
|
||||
{
|
||||
cubeb->m_state_callback(AudioStateEvent::DEFAULT_DEVICE_MAYBE_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,9 +44,8 @@ private:
|
||||
|
||||
atomic_t<bool> m_reset_req = false;
|
||||
|
||||
shared_mutex m_dev_sw_mutex{};
|
||||
std::string m_current_device{};
|
||||
bool m_default_dev_changed = false;
|
||||
// Protected by callback mutex
|
||||
std::string m_default_device{};
|
||||
|
||||
bool m_dev_collection_cb_enabled = false;
|
||||
|
||||
@@ -55,8 +54,13 @@ private:
|
||||
static void state_cb(cubeb_stream* stream, void* user_ptr, cubeb_state state);
|
||||
static void device_collection_changed_cb(cubeb* context, void* user_ptr);
|
||||
|
||||
void CloseUnlocked();
|
||||
struct device_handle
|
||||
{
|
||||
cubeb_devid handle{};
|
||||
std::string id;
|
||||
u32 ch_cnt{};
|
||||
};
|
||||
|
||||
std::tuple<cubeb_devid, std::string /* dev_ident */, u32 /* ch_cnt */> GetDevice(std::string_view dev_id = "");
|
||||
std::tuple<cubeb_devid, std::string /* dev_ident */, u32 /* ch_cnt */> GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt);
|
||||
device_handle GetDevice(std::string_view dev_id = "");
|
||||
device_handle GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt);
|
||||
};
|
||||
|
||||
@@ -234,7 +234,7 @@ void FAudioBackend::OnVoiceProcessingPassStart_func(FAudioVoiceCallback *cb_obj,
|
||||
FAudioBackend *faudio = static_cast<FAudioBackend *>(cb_obj);
|
||||
|
||||
std::unique_lock lock(faudio->m_cb_mutex, std::defer_lock);
|
||||
if (BytesRequired && !faudio->m_reset_req.observe() && lock.try_lock() && faudio->m_write_callback && faudio->m_playing)
|
||||
if (BytesRequired && !faudio->m_reset_req.observe() && lock.try_lock_for(std::chrono::microseconds{50}) && faudio->m_write_callback && faudio->m_playing)
|
||||
{
|
||||
ensure(BytesRequired <= faudio->m_data_buf.size(), "FAudio internal buffer is too small. Report to developers!");
|
||||
|
||||
|
||||
@@ -80,12 +80,6 @@ XAudio2Backend::XAudio2Backend()
|
||||
return;
|
||||
}
|
||||
|
||||
if (HRESULT hr = enumerator->RegisterEndpointNotificationCallback(this); FAILED(hr))
|
||||
{
|
||||
XAudio.error("RegisterEndpointNotificationCallback() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
return;
|
||||
}
|
||||
|
||||
// All succeeded, "commit"
|
||||
m_xaudio2_instance = std::move(instance);
|
||||
m_device_enumerator = std::move(enumerator);
|
||||
@@ -125,7 +119,7 @@ bool XAudio2Backend::Operational()
|
||||
|
||||
bool XAudio2Backend::DefaultDeviceChanged()
|
||||
{
|
||||
std::lock_guard lock{m_dev_sw_mutex};
|
||||
std::lock_guard lock{m_state_cb_mutex};
|
||||
return !m_reset_req.observe() && m_default_dev_changed;
|
||||
}
|
||||
|
||||
@@ -162,9 +156,12 @@ void XAudio2Backend::CloseUnlocked()
|
||||
m_master_voice = nullptr;
|
||||
}
|
||||
|
||||
m_device_enumerator->UnregisterEndpointNotificationCallback(this);
|
||||
|
||||
m_playing = false;
|
||||
m_last_sample.fill(0);
|
||||
|
||||
std::lock_guard lock(m_state_cb_mutex);
|
||||
m_default_dev_changed = false;
|
||||
m_current_device.clear();
|
||||
}
|
||||
@@ -172,7 +169,6 @@ void XAudio2Backend::CloseUnlocked()
|
||||
void XAudio2Backend::Close()
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
std::lock_guard dev_sw_lock{m_dev_sw_mutex};
|
||||
CloseUnlocked();
|
||||
}
|
||||
|
||||
@@ -207,7 +203,6 @@ bool XAudio2Backend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSi
|
||||
}
|
||||
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
std::lock_guard dev_sw_lock{m_dev_sw_mutex};
|
||||
CloseUnlocked();
|
||||
|
||||
const bool use_default_device = dev_id.empty() || dev_id == audio_device_enumerator::DEFAULT_DEV_ID;
|
||||
@@ -282,6 +277,13 @@ bool XAudio2Backend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSi
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HRESULT hr = m_device_enumerator->RegisterEndpointNotificationCallback(this); FAILED(hr))
|
||||
{
|
||||
XAudio.error("RegisterEndpointNotificationCallback() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
CloseUnlocked();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HRESULT hr = m_source_voice->SetVolume(1.0f); FAILED(hr))
|
||||
{
|
||||
XAudio.error("SetVolume() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
|
||||
@@ -331,7 +333,7 @@ f64 XAudio2Backend::GetCallbackFrameLen()
|
||||
void XAudio2Backend::OnVoiceProcessingPassStart(UINT32 BytesRequired)
|
||||
{
|
||||
std::unique_lock lock(m_cb_mutex, std::defer_lock);
|
||||
if (BytesRequired && !m_reset_req.observe() && lock.try_lock() && m_write_callback && m_playing)
|
||||
if (BytesRequired && !m_reset_req.observe() && lock.try_lock_for(std::chrono::microseconds{50}) && m_write_callback && m_playing)
|
||||
{
|
||||
ensure(BytesRequired <= m_data_buf.size(), "XAudio internal buffer is too small. Report to developers!");
|
||||
|
||||
@@ -386,7 +388,7 @@ HRESULT XAudio2Backend::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWS
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
std::lock_guard lock{m_dev_sw_mutex};
|
||||
std::lock_guard lock(m_state_cb_mutex);
|
||||
|
||||
// Non default device is used
|
||||
if (m_current_device.empty())
|
||||
@@ -404,7 +406,7 @@ HRESULT XAudio2Backend::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWS
|
||||
|
||||
if (m_state_callback)
|
||||
{
|
||||
m_state_callback(AudioStateEvent::DEFAULT_DEVICE_CHANGED);
|
||||
m_state_callback(AudioStateEvent::DEFAULT_DEVICE_MAYBE_CHANGED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ private:
|
||||
|
||||
Microsoft::WRL::ComPtr<IMMDeviceEnumerator> m_device_enumerator{};
|
||||
|
||||
shared_mutex m_dev_sw_mutex{};
|
||||
// Protected by state callback mutex
|
||||
std::string m_current_device{};
|
||||
bool m_default_dev_changed = false;
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ target_include_directories(rpcs3_emu
|
||||
PUBLIC
|
||||
${RPCS3_SRC_DIR})
|
||||
|
||||
|
||||
# Utilities
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
../util/atomic.cpp
|
||||
@@ -117,7 +116,6 @@ target_sources(rpcs3_emu PRIVATE
|
||||
../Loader/TRP.cpp
|
||||
)
|
||||
|
||||
|
||||
# Audio
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
Audio/audio_resampler.cpp
|
||||
@@ -188,6 +186,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
Cell/lv2/sys_event.cpp
|
||||
Cell/lv2/sys_event_flag.cpp
|
||||
Cell/lv2/sys_fs.cpp
|
||||
Cell/lv2/sys_game.cpp
|
||||
Cell/lv2/sys_gamepad.cpp
|
||||
Cell/lv2/sys_gpio.cpp
|
||||
Cell/lv2/sys_hid.cpp
|
||||
@@ -339,7 +338,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
Cell/Modules/sceNpUtil.cpp
|
||||
Cell/Modules/StaticHLE.cpp
|
||||
Cell/Modules/sys_crashdump.cpp
|
||||
Cell/Modules/sys_game.cpp
|
||||
Cell/Modules/sys_game_.cpp
|
||||
Cell/Modules/sys_heap.cpp
|
||||
Cell/Modules/sys_io_.cpp
|
||||
Cell/Modules/sys_libc_.cpp
|
||||
@@ -415,7 +414,6 @@ target_sources(rpcs3_emu PRIVATE
|
||||
Memory/vm.cpp
|
||||
)
|
||||
|
||||
|
||||
# RSX
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
RSX/gcm_enums.cpp
|
||||
@@ -467,6 +465,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
RSX/Capture/rsx_replay.cpp
|
||||
RSX/GL/glutils/blitter.cpp
|
||||
RSX/GL/glutils/buffer_object.cpp
|
||||
RSX/GL/glutils/capabilities.cpp
|
||||
RSX/GL/glutils/common.cpp
|
||||
RSX/GL/glutils/fbo.cpp
|
||||
RSX/GL/glutils/image.cpp
|
||||
@@ -550,7 +549,7 @@ target_link_libraries(rpcs3_emu
|
||||
if(APPLE)
|
||||
check_function_exists(clock_gettime HAVE_CLOCK_GETTIME)
|
||||
if (${HAVE_CLOCK_GETTIME})
|
||||
target_compile_definitions(rpcs3_emu PUBLIC -DHAVE_CLOCK_GETTIME)
|
||||
target_compile_definitions(rpcs3_emu PUBLIC -DHAVE_CLOCK_GETTIME)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
+142
-35
@@ -5,6 +5,7 @@
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/system_config.h"
|
||||
#include "Emu/Memory/vm_locking.h"
|
||||
#include "Emu/Memory/vm_reservation.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/GDB.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
@@ -54,6 +55,9 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
|
||||
case cpu_flag::memory: return "mem";
|
||||
case cpu_flag::pending: return "pend";
|
||||
case cpu_flag::pending_recheck: return "pend-re";
|
||||
case cpu_flag::notify: return "ntf";
|
||||
case cpu_flag::yield: return "y";
|
||||
case cpu_flag::preempt: return "PREEMPT";
|
||||
case cpu_flag::dbg_global_pause: return "G-PAUSE";
|
||||
case cpu_flag::dbg_pause: return "PAUSE";
|
||||
case cpu_flag::dbg_step: return "STEP";
|
||||
@@ -84,15 +88,18 @@ struct cpu_prof
|
||||
// Total number of samples
|
||||
u64 samples = 0, idle = 0;
|
||||
|
||||
// Avoid printing replicas
|
||||
bool printed = false;
|
||||
// Avoid printing replicas or when not much changed
|
||||
u64 new_samples = 0;
|
||||
|
||||
static constexpr u64 min_print_samples = 500;
|
||||
static constexpr u64 min_print_all_samples = min_print_samples * 20;
|
||||
|
||||
void reset()
|
||||
{
|
||||
freq.clear();
|
||||
samples = 0;
|
||||
idle = 0;
|
||||
printed = false;
|
||||
new_samples = 0;
|
||||
}
|
||||
|
||||
static std::string format(const std::multimap<u64, u64, std::greater<u64>>& chart, u64 samples, u64 idle, bool extended_print = false)
|
||||
@@ -128,8 +135,13 @@ struct cpu_prof
|
||||
// Print info
|
||||
void print(const std::shared_ptr<cpu_thread>& ptr)
|
||||
{
|
||||
if (printed || samples == idle)
|
||||
if (new_samples < min_print_samples || samples == idle)
|
||||
{
|
||||
if (cpu_flag::exit - ptr->state)
|
||||
{
|
||||
profiler.notice("Thread \"%s\" [0x%08x]: %u samples, %u new (%.4f%% idle): Not enough new samples have been collected since the last print.", ptr->get_name(), ptr->id, samples, new_samples, 100. * idle / samples);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -143,13 +155,22 @@ struct cpu_prof
|
||||
|
||||
// Print results
|
||||
const std::string results = format(chart, samples, idle);
|
||||
profiler.notice("Thread \"%s\" [0x%08x]: %u samples (%.4f%% idle):%s", ptr->get_name(), ptr->id, samples, 100. * idle / samples, results);
|
||||
profiler.notice("Thread \"%s\" [0x%08x]: %u samples, %u new (%.4f%% idle):%s", ptr->get_name(), ptr->id, samples, new_samples, 100. * idle / samples, results);
|
||||
|
||||
printed = true;
|
||||
new_samples = 0;
|
||||
}
|
||||
|
||||
static void print_all(const std::unordered_map<std::shared_ptr<cpu_thread>, sample_info>& threads)
|
||||
static void print_all(std::unordered_map<std::shared_ptr<cpu_thread>, sample_info>& threads)
|
||||
{
|
||||
u64 new_samples = 0;
|
||||
|
||||
// Print all results and cleanup
|
||||
for (auto& [ptr, info] : threads)
|
||||
{
|
||||
new_samples += info.new_samples;
|
||||
info.print(ptr);
|
||||
}
|
||||
|
||||
std::multimap<u64, u64, std::greater<u64>> chart;
|
||||
|
||||
std::unordered_map<u64, u64, value_hash<u64>> freq;
|
||||
@@ -158,7 +179,7 @@ struct cpu_prof
|
||||
|
||||
for (auto& [_, info] : threads)
|
||||
{
|
||||
// This function collects thread information regardless of 'printed' member state
|
||||
// This function collects thread information regardless of 'new_samples' member state
|
||||
for (auto& [name, count] : info.freq)
|
||||
{
|
||||
freq[name] += count;
|
||||
@@ -168,18 +189,24 @@ struct cpu_prof
|
||||
idle += info.idle;
|
||||
}
|
||||
|
||||
for (auto& [name, count] : freq)
|
||||
{
|
||||
chart.emplace(count, name);
|
||||
}
|
||||
|
||||
if (samples == idle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (new_samples < min_print_all_samples && thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
profiler.notice("All Threads: %u samples, %u new (%.4f%% idle): Not enough new samples have been collected since the last print.", samples, new_samples, 100. * idle / samples);
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& [name, count] : freq)
|
||||
{
|
||||
chart.emplace(count, name);
|
||||
}
|
||||
|
||||
const std::string results = format(chart, samples, idle, true);
|
||||
profiler.notice("All Threads: %u samples (%.4f%% idle):%s", samples, 100. * idle / samples, results);
|
||||
profiler.notice("All Threads: %u samples, %u new (%.4f%% idle):%s", samples, new_samples, 100. * idle / samples, results);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -240,7 +267,7 @@ struct cpu_prof
|
||||
// Sample active threads
|
||||
for (auto& [ptr, info] : threads)
|
||||
{
|
||||
if (cpu_flag::exit - ptr->state)
|
||||
if (auto state = +ptr->state; cpu_flag::exit - state)
|
||||
{
|
||||
// Get short function hash
|
||||
const u64 name = atomic_storage<u64>::load(ptr->block_hash);
|
||||
@@ -248,9 +275,10 @@ struct cpu_prof
|
||||
// Append occurrence
|
||||
info.samples++;
|
||||
|
||||
if (auto state = +ptr->state; !::is_paused(state) && !::is_stopped(state) && cpu_flag::wait - state)
|
||||
if (cpu_flag::wait - state)
|
||||
{
|
||||
info.freq[name]++;
|
||||
info.new_samples++;
|
||||
|
||||
// Append verification time to fixed common name 0000000...chunk-0x3fffc
|
||||
if (name >> 16 && (name & 0xffff) == 0)
|
||||
@@ -258,6 +286,12 @@ struct cpu_prof
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state & (cpu_flag::dbg_pause + cpu_flag::dbg_global_pause))
|
||||
{
|
||||
// Idle state caused by emulation pause is not accounted for
|
||||
continue;
|
||||
}
|
||||
|
||||
info.idle++;
|
||||
}
|
||||
}
|
||||
@@ -271,11 +305,13 @@ struct cpu_prof
|
||||
{
|
||||
profiler.success("Flushing profiling results...");
|
||||
|
||||
// Print all results and cleanup
|
||||
for (auto& [ptr, info] : threads)
|
||||
{
|
||||
info.print(ptr);
|
||||
}
|
||||
sample_info::print_all(threads);
|
||||
}
|
||||
|
||||
if (Emu.IsPaused())
|
||||
{
|
||||
thread_ctrl::wait_for(5000);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Wait, roughly for 20µs
|
||||
@@ -283,11 +319,6 @@ struct cpu_prof
|
||||
}
|
||||
|
||||
// Print all remaining results
|
||||
for (auto& [ptr, info] : threads)
|
||||
{
|
||||
info.print(ptr);
|
||||
}
|
||||
|
||||
sample_info::print_all(threads);
|
||||
}
|
||||
|
||||
@@ -574,6 +605,7 @@ void cpu_thread::operator()()
|
||||
if (!(state0 & cpu_flag::stop))
|
||||
{
|
||||
cpu_task();
|
||||
state += cpu_flag::wait;
|
||||
|
||||
if (state & cpu_flag::ret && state.test_and_reset(cpu_flag::ret))
|
||||
{
|
||||
@@ -583,7 +615,7 @@ void cpu_thread::operator()()
|
||||
continue;
|
||||
}
|
||||
|
||||
thread_ctrl::wait_on(state, state0);
|
||||
state.wait(state0);
|
||||
|
||||
if (state & cpu_flag::ret && state.test_and_reset(cpu_flag::ret))
|
||||
{
|
||||
@@ -635,20 +667,22 @@ cpu_thread::cpu_thread(u32 id)
|
||||
|
||||
void cpu_thread::cpu_wait(bs_t<cpu_flag> old)
|
||||
{
|
||||
thread_ctrl::wait_on(state, old);
|
||||
state.wait(old);
|
||||
}
|
||||
|
||||
static atomic_t<u32> s_dummy_atomic = 0;
|
||||
|
||||
bool cpu_thread::check_state() noexcept
|
||||
{
|
||||
bool cpu_sleep_called = false;
|
||||
bool cpu_can_stop = true;
|
||||
bool escape, retval;
|
||||
bool escape{}, retval{};
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Process all flags in a single atomic op
|
||||
bs_t<cpu_flag> state1;
|
||||
const auto state0 = state.fetch_op([&](bs_t<cpu_flag>& flags)
|
||||
auto state0 = state.fetch_op([&](bs_t<cpu_flag>& flags)
|
||||
{
|
||||
bool store = false;
|
||||
|
||||
@@ -688,7 +722,6 @@ bool cpu_thread::check_state() noexcept
|
||||
{
|
||||
// Sticky flag, indicates check_state() is not allowed to return true
|
||||
flags -= cpu_flag::temp;
|
||||
flags -= cpu_flag::wait;
|
||||
cpu_can_stop = false;
|
||||
store = true;
|
||||
}
|
||||
@@ -730,7 +763,7 @@ bool cpu_thread::check_state() noexcept
|
||||
if (!is_stopped(flags) && flags.none_of(cpu_flag::ret))
|
||||
{
|
||||
// Check pause flags which hold thread inside check_state (ignore suspend/debug flags on cpu_flag::temp)
|
||||
if (flags & (cpu_flag::pause + cpu_flag::memory) || (cpu_can_stop && flags & (cpu_flag::dbg_global_pause + cpu_flag::dbg_pause + cpu_flag::suspend)))
|
||||
if (flags & (cpu_flag::pause + cpu_flag::memory) || (cpu_can_stop && flags & (cpu_flag::dbg_global_pause + cpu_flag::dbg_pause + cpu_flag::suspend + cpu_flag::yield + cpu_flag::preempt)))
|
||||
{
|
||||
if (!(flags & cpu_flag::wait))
|
||||
{
|
||||
@@ -738,6 +771,12 @@ bool cpu_thread::check_state() noexcept
|
||||
store = true;
|
||||
}
|
||||
|
||||
if (flags & (cpu_flag::yield + cpu_flag::preempt) && cpu_can_stop)
|
||||
{
|
||||
flags -= (cpu_flag::yield + cpu_flag::preempt);
|
||||
store = true;
|
||||
}
|
||||
|
||||
escape = false;
|
||||
state1 = flags;
|
||||
return store;
|
||||
@@ -753,9 +792,9 @@ bool cpu_thread::check_state() noexcept
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cpu_can_stop && !(flags & cpu_flag::wait))
|
||||
if (flags & cpu_flag::wait)
|
||||
{
|
||||
flags += cpu_flag::wait;
|
||||
flags -= cpu_flag::wait;
|
||||
store = true;
|
||||
}
|
||||
|
||||
@@ -767,6 +806,34 @@ bool cpu_thread::check_state() noexcept
|
||||
return store;
|
||||
}).first;
|
||||
|
||||
if (state0 & cpu_flag::preempt && cpu_can_stop)
|
||||
{
|
||||
if (cpu_flag::wait - state0)
|
||||
{
|
||||
if (!escape || !retval)
|
||||
{
|
||||
// Yield itself
|
||||
state0 += cpu_flag::yield;
|
||||
escape = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (const u128 bits = s_cpu_bits)
|
||||
{
|
||||
reader_lock lock(s_cpu_lock);
|
||||
|
||||
cpu_counter::for_all_cpu(bits & s_cpu_bits, [](cpu_thread* cpu)
|
||||
{
|
||||
if (cpu->state.none_of(cpu_flag::wait + cpu_flag::yield))
|
||||
{
|
||||
cpu->state += cpu_flag::yield;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (escape)
|
||||
{
|
||||
if (s_tls_thread_slot == umax && !retval)
|
||||
@@ -855,6 +922,32 @@ bool cpu_thread::check_state() noexcept
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state0 & cpu_flag::yield && cpu_flag::wait - state0 && cpu_can_stop)
|
||||
{
|
||||
if (auto spu = try_get<spu_thread>())
|
||||
{
|
||||
if (spu->raddr && spu->rtime == vm::reservation_acquire(spu->raddr) && spu->getllar_spin_count < 10)
|
||||
{
|
||||
// Reservation operation is a critical section (but this may result in false positives)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (auto ppu = try_get<ppu_thread>())
|
||||
{
|
||||
if (ppu->raddr && ppu->rtime == vm::reservation_acquire(ppu->raddr))
|
||||
{
|
||||
// Same
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Short sleep when yield flag is present alone (makes no sense when other methods which can stop thread execution have been done)
|
||||
// Pass a mask of a single bit which is often unused to avoid notifications
|
||||
s_dummy_atomic.wait(0, 1u << 30, atomic_wait_timeout{80'000});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -892,6 +985,14 @@ cpu_thread& cpu_thread::operator=(thread_state)
|
||||
if (old & cpu_flag::wait && old.none_of(cpu_flag::again + cpu_flag::exit))
|
||||
{
|
||||
state.notify_one(cpu_flag::exit);
|
||||
|
||||
if (auto thread = try_get<spu_thread>())
|
||||
{
|
||||
if (u32 resv = atomic_storage<u32>::load(thread->raddr))
|
||||
{
|
||||
vm::reservation_notifier(resv).notify_one();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -1232,13 +1333,19 @@ u32 CPUDisAsm::DisAsmBranchTarget(s32 /*imm*/)
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates()
|
||||
extern bool try_lock_spu_threads_in_a_state_compatible_with_savestates(bool revert_lock)
|
||||
{
|
||||
const u64 start = get_system_time();
|
||||
|
||||
// Attempt to lock for half a second, if somehow takes longer abort it
|
||||
do
|
||||
{
|
||||
if (revert_lock)
|
||||
{
|
||||
// Revert the operation of this function
|
||||
break;
|
||||
}
|
||||
|
||||
if (cpu_thread::suspend_all(nullptr, {}, []()
|
||||
{
|
||||
return idm::select<named_thread<spu_thread>>([](u32, spu_thread& spu)
|
||||
|
||||
@@ -25,6 +25,8 @@ enum class cpu_flag : u32
|
||||
pending, // Thread has postponed work
|
||||
pending_recheck, // Thread needs to recheck if there is pending work before ::pending removal
|
||||
notify, // Flag meant solely to allow atomic notification on state without changing other flags
|
||||
yield, // Thread is being requested to yield its execution time if it's running
|
||||
preempt, // Thread is being requested to preempt the execution of all CPU threads
|
||||
|
||||
dbg_global_pause, // Emulation paused
|
||||
dbg_pause, // Thread paused
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#endif
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/sysinfo.hpp"
|
||||
#include "Utilities/StrFmt.h"
|
||||
#include "Utilities/BitField.h"
|
||||
#include "Utilities/JIT.h"
|
||||
@@ -3442,6 +3443,11 @@ public:
|
||||
template <typename T1, typename T2, typename T3>
|
||||
value_t<u8[16]> vperm2b(T1 a, T2 b, T3 c)
|
||||
{
|
||||
if (!utils::has_fast_vperm2b())
|
||||
{
|
||||
return vperm2b256to128(a, b, c);
|
||||
}
|
||||
|
||||
value_t<u8[16]> result;
|
||||
|
||||
const auto data0 = a.eval(m_ir);
|
||||
|
||||
@@ -162,7 +162,7 @@ bool statichle_handler::check_against_patterns(vm::cptr<u8>& data, u32 size, u32
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto sfunc = &smodule->functions.at(pat.fnid);
|
||||
const auto sfunc = &::at32(smodule->functions, pat.fnid);
|
||||
const u32 target = g_fxo->get<ppu_function_manager>().func_addr(sfunc->index) + 4;
|
||||
|
||||
// write stub
|
||||
|
||||
@@ -454,30 +454,8 @@ public:
|
||||
if (adecIsAtracX(type)) last_pts -= 0x10000; // hack
|
||||
}
|
||||
|
||||
struct AVPacketHolder : AVPacket
|
||||
{
|
||||
AVPacketHolder(u32 data_size)
|
||||
{
|
||||
av_init_packet(this);
|
||||
|
||||
if (data_size)
|
||||
{
|
||||
this->data = static_cast<u8*>(av_calloc(1, data_size + AV_INPUT_BUFFER_PADDING_SIZE));
|
||||
this->size = data_size + AV_INPUT_BUFFER_PADDING_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
this->data = nullptr;
|
||||
this->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
~AVPacketHolder()
|
||||
{
|
||||
av_free(data);
|
||||
}
|
||||
|
||||
} au(0);
|
||||
AVPacket* packet = av_packet_alloc();
|
||||
std::unique_ptr<AVPacket, decltype([](AVPacket* p){av_packet_unref(p);})> packet_(packet);
|
||||
|
||||
if (just_started && just_finished)
|
||||
{
|
||||
@@ -544,8 +522,6 @@ public:
|
||||
just_started = false;
|
||||
}
|
||||
|
||||
bool last_frame = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (Emu.IsStopped() || is_closed)
|
||||
@@ -554,14 +530,7 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
last_frame = av_read_frame(fmt, &au) < 0;
|
||||
if (last_frame)
|
||||
{
|
||||
//break;
|
||||
av_free(au.data);
|
||||
au.data = NULL;
|
||||
au.size = 0;
|
||||
}
|
||||
av_read_frame(fmt, packet);
|
||||
|
||||
struct AdecFrameHolder : AdecFrame
|
||||
{
|
||||
|
||||
@@ -158,6 +158,7 @@ audio_ringbuffer::audio_ringbuffer(cell_audio_config& _cfg)
|
||||
|
||||
cb_ringbuf.set_buf_size(static_cast<u32>(static_cast<u32>(cfg.backend_ch_cnt) * cfg.audio_sampling_rate * cfg.audio_sample_size * buffer_dur_mult));
|
||||
backend->SetWriteCallback(std::bind(&audio_ringbuffer::backend_write_callback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
backend->SetStateCallback(std::bind(&audio_ringbuffer::backend_state_callback, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
audio_ringbuffer::~audio_ringbuffer()
|
||||
@@ -191,6 +192,14 @@ u32 audio_ringbuffer::backend_write_callback(u32 size, void *buf)
|
||||
return static_cast<u32>(cb_ringbuf.pop(buf, size, true));
|
||||
}
|
||||
|
||||
void audio_ringbuffer::backend_state_callback(AudioStateEvent event)
|
||||
{
|
||||
if (event == AudioStateEvent::DEFAULT_DEVICE_MAYBE_CHANGED)
|
||||
{
|
||||
backend_device_changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
u64 audio_ringbuffer::get_timestamp()
|
||||
{
|
||||
return get_system_time();
|
||||
@@ -683,7 +692,6 @@ void cell_audio_thread::reset_counters()
|
||||
m_start_time = ringbuffer->get_timestamp();
|
||||
m_last_period_end = m_start_time;
|
||||
m_dynamic_period = 0;
|
||||
m_backend_failed = false;
|
||||
m_audio_should_restart = true;
|
||||
}
|
||||
|
||||
@@ -720,9 +728,13 @@ void cell_audio_thread::operator()()
|
||||
|
||||
u32 untouched_expected = 0;
|
||||
|
||||
u32 loop_count = 0;
|
||||
|
||||
// Main cellAudio loop
|
||||
while (thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
loop_count++;
|
||||
|
||||
const auto update_req = m_update_configuration.observe();
|
||||
if (update_req != audio_backend_update::NONE)
|
||||
{
|
||||
@@ -735,37 +747,8 @@ void cell_audio_thread::operator()()
|
||||
update_config(update_req == audio_backend_update::ALL);
|
||||
}
|
||||
|
||||
if (!ringbuffer->get_operational_status())
|
||||
{
|
||||
if (m_backend_failed)
|
||||
{
|
||||
thread_ctrl::wait_for(500 * 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
cellAudio.warning("Backend stopped unexpectedly (likely device change). Attempting to recover...");
|
||||
}
|
||||
|
||||
update_config(true);
|
||||
m_backend_failed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ringbuffer->device_changed())
|
||||
{
|
||||
cellAudio.warning("Default device changed, attempting to switch...");
|
||||
update_config(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_backend_failed)
|
||||
{
|
||||
cellAudio.warning("Backend recovered");
|
||||
m_backend_failed = false;
|
||||
}
|
||||
|
||||
const bool emu_paused = Emu.IsPaused();
|
||||
const u64 timestamp = ringbuffer->update(emu_paused);
|
||||
const u64 timestamp = ringbuffer->update(emu_paused || m_backend_failed);
|
||||
|
||||
if (emu_paused)
|
||||
{
|
||||
@@ -803,6 +786,36 @@ void cell_audio_thread::operator()()
|
||||
continue;
|
||||
}
|
||||
|
||||
bool operational = ringbuffer->get_operational_status();
|
||||
|
||||
if (!operational && loop_count % 128 == 0)
|
||||
{
|
||||
update_config(true);
|
||||
operational = ringbuffer->get_operational_status();
|
||||
}
|
||||
|
||||
if (ringbuffer->device_changed())
|
||||
{
|
||||
cellAudio.warning("Default device changed, attempting to switch...");
|
||||
update_config(false);
|
||||
|
||||
if (operational != ringbuffer->get_operational_status())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_backend_failed && !operational)
|
||||
{
|
||||
cellAudio.error("Backend stopped unexpectedly (likely device change). Attempting to recover...");
|
||||
m_backend_failed = true;
|
||||
}
|
||||
else if (m_backend_failed && operational)
|
||||
{
|
||||
cellAudio.success("Backend recovered");
|
||||
m_backend_failed = false;
|
||||
}
|
||||
|
||||
if (!cfg.buffering_enabled)
|
||||
{
|
||||
const u64 period_end = (m_counter * cfg.audio_block_period) + m_start_time;
|
||||
@@ -906,7 +919,7 @@ void cell_audio_thread::operator()()
|
||||
// As such, if the game doesn't touch buffers for too long we advance time hoping the game recovers
|
||||
if (
|
||||
(untouched == active_ports && time_since_last_period > cfg.fully_untouched_timeout) ||
|
||||
(time_since_last_period > cfg.partially_untouched_timeout)
|
||||
(time_since_last_period > cfg.partially_untouched_timeout) || g_cfg.audio.disable_sampling_skip
|
||||
)
|
||||
{
|
||||
// There's no audio in the buffers, simply advance time and hope the game recovers
|
||||
@@ -1859,12 +1872,13 @@ error_code cellAudioAdd6chData(u32 portNum, vm::ptr<float> src, float volume)
|
||||
{
|
||||
for (u32 i = 0; i < CELL_AUDIO_BLOCK_SAMPLES; i++)
|
||||
{
|
||||
// Channel order in src is Front Left, Center, Front Right, Surround Left, Surround Right, LFE
|
||||
dst[i * 8 + 0] += src[i * 6 + 0] * volume; // mix L ch
|
||||
dst[i * 8 + 1] += src[i * 6 + 1] * volume; // mix R ch
|
||||
dst[i * 8 + 2] += src[i * 6 + 2] * volume; // mix center
|
||||
dst[i * 8 + 3] += src[i * 6 + 3] * volume; // mix LFE
|
||||
dst[i * 8 + 4] += src[i * 6 + 4] * volume; // mix rear L
|
||||
dst[i * 8 + 5] += src[i * 6 + 5] * volume; // mix rear R
|
||||
dst[i * 8 + 1] += src[i * 6 + 2] * volume; // mix R ch
|
||||
dst[i * 8 + 2] += src[i * 6 + 1] * volume; // mix center
|
||||
dst[i * 8 + 3] += src[i * 6 + 5] * volume; // mix LFE
|
||||
dst[i * 8 + 4] += src[i * 6 + 3] * volume; // mix rear L
|
||||
dst[i * 8 + 5] += src[i * 6 + 4] * volume; // mix rear R
|
||||
//dst[i * 8 + 6] += 0.0f; // side L
|
||||
//dst[i * 8 + 7] += 0.0f; // side R
|
||||
}
|
||||
|
||||
@@ -292,6 +292,7 @@ private:
|
||||
audio_resampler resampler{};
|
||||
|
||||
atomic_t<bool> backend_active = false;
|
||||
atomic_t<bool> backend_device_changed = false;
|
||||
bool playing = false;
|
||||
|
||||
u64 update_timestamp = 0;
|
||||
@@ -310,6 +311,7 @@ private:
|
||||
|
||||
void commit_data(f32* buf, u32 sample_cnt);
|
||||
u32 backend_write_callback(u32 size, void *buf);
|
||||
void backend_state_callback(AudioStateEvent event);
|
||||
|
||||
public:
|
||||
audio_ringbuffer(cell_audio_config &cfg);
|
||||
@@ -345,9 +347,9 @@ public:
|
||||
return backend->Operational();
|
||||
}
|
||||
|
||||
bool device_changed() const
|
||||
bool device_changed()
|
||||
{
|
||||
return backend->DefaultDeviceChanged();
|
||||
return backend_device_changed.test_and_reset() && backend->DefaultDeviceChanged();
|
||||
}
|
||||
|
||||
std::string_view get_backend_name() const
|
||||
|
||||
@@ -33,8 +33,8 @@ void fmt_class_string<CellAudioOutError>::format(std::string& out, u64 arg)
|
||||
|
||||
audio_out_configuration::audio_out_configuration()
|
||||
{
|
||||
audio_out& primary_output = out.at(CELL_AUDIO_OUT_PRIMARY);
|
||||
audio_out& secondary_output = out.at(CELL_AUDIO_OUT_SECONDARY);
|
||||
audio_out& primary_output = ::at32(out, CELL_AUDIO_OUT_PRIMARY);
|
||||
audio_out& secondary_output = ::at32(out, CELL_AUDIO_OUT_SECONDARY);
|
||||
|
||||
std::vector<CellAudioOutSoundMode>& primary_modes = primary_output.sound_modes;
|
||||
std::vector<CellAudioOutSoundMode>& secondary_modes = secondary_output.sound_modes;
|
||||
@@ -58,8 +58,8 @@ audio_out_configuration::audio_out_configuration()
|
||||
|
||||
const auto add_sound_mode = [&](u32 index, u8 type, u8 channel, u8 fs, u32 layout, bool supported)
|
||||
{
|
||||
audio_out& output = out.at(index);
|
||||
bool& selected = initial_mode_selected.at(index);
|
||||
audio_out& output = ::at32(out, index);
|
||||
bool& selected = ::at32(initial_mode_selected, index);
|
||||
|
||||
CellAudioOutSoundMode mode{};
|
||||
mode.type = type;
|
||||
@@ -103,7 +103,7 @@ audio_out_configuration::audio_out_configuration()
|
||||
{
|
||||
// Linear PCM 5.1 Ch. 48 kHz
|
||||
add_sound_mode(CELL_AUDIO_OUT_PRIMARY, CELL_AUDIO_OUT_CODING_TYPE_LPCM, CELL_AUDIO_OUT_CHNUM_6, CELL_AUDIO_OUT_FS_48KHZ, CELL_AUDIO_OUT_SPEAKER_LAYOUT_6CH_LREClr, supports_lpcm_5_1);
|
||||
|
||||
|
||||
// Dolby Digital 5.1 Ch.
|
||||
add_sound_mode(CELL_AUDIO_OUT_PRIMARY, CELL_AUDIO_OUT_CODING_TYPE_AC3, CELL_AUDIO_OUT_CHNUM_6, CELL_AUDIO_OUT_FS_48KHZ, CELL_AUDIO_OUT_SPEAKER_LAYOUT_6CH_LREClr, supports_ac3);
|
||||
|
||||
@@ -161,8 +161,8 @@ audio_out_configuration::audio_out_configuration()
|
||||
// The secondary output only supports Linear PCM 2 Ch.
|
||||
add_sound_mode(CELL_AUDIO_OUT_SECONDARY, CELL_AUDIO_OUT_CODING_TYPE_LPCM, CELL_AUDIO_OUT_CHNUM_2, CELL_AUDIO_OUT_FS_48KHZ, CELL_AUDIO_OUT_SPEAKER_LAYOUT_2CH, true);
|
||||
|
||||
ensure(!primary_modes.empty() && initial_mode_selected.at(CELL_AUDIO_OUT_PRIMARY));
|
||||
ensure(!secondary_modes.empty() && initial_mode_selected.at(CELL_AUDIO_OUT_SECONDARY));
|
||||
ensure(!primary_modes.empty() && ::at32(initial_mode_selected, CELL_AUDIO_OUT_PRIMARY));
|
||||
ensure(!secondary_modes.empty() && ::at32(initial_mode_selected, CELL_AUDIO_OUT_SECONDARY));
|
||||
|
||||
for (const CellAudioOutSoundMode& mode : primary_modes)
|
||||
{
|
||||
@@ -238,7 +238,7 @@ error_code cellAudioOutGetSoundAvailability(u32 audioOut, u32 type, u32 fs, u32
|
||||
// Check if the requested audio parameters are available and find the max supported channel count
|
||||
audio_out_configuration& cfg = g_fxo->get<audio_out_configuration>();
|
||||
std::lock_guard lock(cfg.mtx);
|
||||
const audio_out_configuration::audio_out& out = cfg.out.at(audioOut);
|
||||
const audio_out_configuration::audio_out& out = ::at32(cfg.out, audioOut);
|
||||
|
||||
for (const CellAudioOutSoundMode& mode : out.sound_modes)
|
||||
{
|
||||
@@ -265,7 +265,7 @@ error_code cellAudioOutGetSoundAvailability2(u32 audioOut, u32 type, u32 fs, u32
|
||||
// Check if the requested audio parameters are available
|
||||
audio_out_configuration& cfg = g_fxo->get<audio_out_configuration>();
|
||||
std::lock_guard lock(cfg.mtx);
|
||||
const audio_out_configuration::audio_out& out = cfg.out.at(audioOut);
|
||||
const audio_out_configuration::audio_out& out = ::at32(cfg.out, audioOut);
|
||||
|
||||
for (const CellAudioOutSoundMode& mode : out.sound_modes)
|
||||
{
|
||||
@@ -319,7 +319,7 @@ error_code cellAudioOutGetState(u32 audioOut, u32 deviceIndex, vm::ptr<CellAudio
|
||||
{
|
||||
audio_out_configuration& cfg = g_fxo->get<audio_out_configuration>();
|
||||
std::lock_guard lock(cfg.mtx);
|
||||
const audio_out_configuration::audio_out& out = cfg.out.at(audioOut);
|
||||
const audio_out_configuration::audio_out& out = ::at32(cfg.out, audioOut);
|
||||
|
||||
_state.state = out.state;
|
||||
_state.encoder = out.encoder;
|
||||
@@ -360,7 +360,7 @@ error_code cellAudioOutConfigure(u32 audioOut, vm::ptr<CellAudioOutConfiguration
|
||||
{
|
||||
std::lock_guard lock(cfg.mtx);
|
||||
|
||||
audio_out_configuration::audio_out& out = cfg.out.at(audioOut);
|
||||
audio_out_configuration::audio_out& out = ::at32(cfg.out, audioOut);
|
||||
|
||||
// Apparently the set config does not necessarily have to exist in the list of sound modes.
|
||||
|
||||
@@ -406,7 +406,7 @@ error_code cellAudioOutConfigure(u32 audioOut, vm::ptr<CellAudioOutConfiguration
|
||||
audio_out_configuration& cfg = g_fxo->get<audio_out_configuration>();
|
||||
{
|
||||
std::lock_guard lock(cfg.mtx);
|
||||
cfg.out.at(audioOut).state = CELL_AUDIO_OUT_OUTPUT_STATE_DISABLED;
|
||||
::at32(cfg.out, audioOut).state = CELL_AUDIO_OUT_OUTPUT_STATE_DISABLED;
|
||||
}
|
||||
|
||||
audio::configure_audio(true);
|
||||
@@ -414,7 +414,7 @@ error_code cellAudioOutConfigure(u32 audioOut, vm::ptr<CellAudioOutConfiguration
|
||||
|
||||
{
|
||||
std::lock_guard lock(cfg.mtx);
|
||||
cfg.out.at(audioOut).state = CELL_AUDIO_OUT_OUTPUT_STATE_ENABLED;
|
||||
::at32(cfg.out, audioOut).state = CELL_AUDIO_OUT_OUTPUT_STATE_ENABLED;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -455,7 +455,7 @@ error_code cellAudioOutGetConfiguration(u32 audioOut, vm::ptr<CellAudioOutConfig
|
||||
audio_out_configuration& cfg = g_fxo->get<audio_out_configuration>();
|
||||
std::lock_guard lock(cfg.mtx);
|
||||
|
||||
const audio_out_configuration::audio_out& out = cfg.out.at(audioOut);
|
||||
const audio_out_configuration::audio_out& out = ::at32(cfg.out, audioOut);
|
||||
|
||||
// Return the active config.
|
||||
CellAudioOutConfiguration _config{};
|
||||
@@ -516,7 +516,7 @@ error_code cellAudioOutGetDeviceInfo(u32 audioOut, u32 deviceIndex, vm::ptr<Cell
|
||||
audio_out_configuration& cfg = g_fxo->get<audio_out_configuration>();
|
||||
std::lock_guard lock(cfg.mtx);
|
||||
ensure(audioOut < cfg.out.size());
|
||||
const audio_out_configuration::audio_out& out = cfg.out.at(audioOut);
|
||||
const audio_out_configuration::audio_out& out = ::at32(cfg.out, audioOut);
|
||||
ensure(out.sound_modes.size() <= 16);
|
||||
|
||||
CellAudioOutDeviceInfo _info{};
|
||||
@@ -528,7 +528,7 @@ error_code cellAudioOutGetDeviceInfo(u32 audioOut, u32 deviceIndex, vm::ptr<Cell
|
||||
|
||||
for (usz i = 0; i < out.sound_modes.size(); i++)
|
||||
{
|
||||
_info.availableModes[i] = out.sound_modes.at(i);
|
||||
_info.availableModes[i] = ::at32(out.sound_modes, i);
|
||||
}
|
||||
|
||||
*info = _info;
|
||||
@@ -557,7 +557,7 @@ error_code cellAudioOutSetCopyControl(u32 audioOut, u32 control)
|
||||
audio_out_configuration& cfg = g_fxo->get<audio_out_configuration>();
|
||||
std::lock_guard lock(cfg.mtx);
|
||||
|
||||
cfg.out.at(audioOut).copy_control = control;
|
||||
::at32(cfg.out, audioOut).copy_control = control;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ static error_code check_camera_info(const VariantOfCellCameraInfo& info)
|
||||
return CELL_CAMERA_ERROR_BAD_FRAMERATE;
|
||||
}
|
||||
|
||||
auto check_fps = [fps = info.framerate](const std::vector<s32>& range)
|
||||
auto check_fps = [fps = info.framerate](std::initializer_list<s32> range)
|
||||
{
|
||||
return std::find(range.begin(), range.end(), fps) != range.end();
|
||||
};
|
||||
|
||||
@@ -477,5 +477,3 @@ struct gem_camera_shared
|
||||
atomic_t<s32> size{0};
|
||||
atomic_t<CellCameraFormat> format{CELL_CAMERA_RAW8};
|
||||
};
|
||||
|
||||
static inline s32 get_video_buffer_size(s32 width, s32 height);
|
||||
|
||||
@@ -52,7 +52,7 @@ struct cross_controller
|
||||
|
||||
close_msg_dialog();
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
sysutil_register_cb([this, status](ppu_thread& ppu) -> s32
|
||||
{
|
||||
callback(ppu, CELL_CROSS_CONTROLLER_STATUS_FINALIZED, status, vm::null, userdata);
|
||||
return CELL_OK;
|
||||
@@ -74,9 +74,9 @@ struct cross_controller
|
||||
// TODO: Show icons from comboplay_plugin.rco in dialog. Maybe use a new dialog or add an optional icon to this one.
|
||||
error_code res = open_msg_dialog(false, CELL_MSGDIALOG_TYPE_DISABLE_CANCEL_OFF, vm::make_str(msg), msg_dialog_callback, userdata);
|
||||
|
||||
sysutil_register_cb([=](ppu_thread& ppu) -> s32
|
||||
sysutil_register_cb([this, res](ppu_thread& ppu) -> s32
|
||||
{
|
||||
callback(ppu, CELL_CROSS_CONTROLLER_STATUS_INITIALIZED, res == CELL_OK ? CELL_OK : CELL_CROSS_CONTROLLER_ERROR_INTERNAL, vm::null, userdata);
|
||||
callback(ppu, CELL_CROSS_CONTROLLER_STATUS_INITIALIZED, res == CELL_OK ? +CELL_OK : +CELL_CROSS_CONTROLLER_ERROR_INTERNAL, vm::null, userdata);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
|
||||
@@ -230,17 +230,12 @@ disc_change_manager::~disc_change_manager()
|
||||
|
||||
error_code disc_change_manager::register_callbacks(vm::ptr<CellGameDiscEjectCallback> func_eject, vm::ptr<CellGameDiscInsertCallback> func_insert)
|
||||
{
|
||||
if (!func_eject || !func_insert)
|
||||
{
|
||||
return CELL_GAME_ERROR_PARAM;
|
||||
}
|
||||
|
||||
std::lock_guard lock(mtx);
|
||||
|
||||
eject_callback = func_eject;
|
||||
insert_callback = func_insert;
|
||||
|
||||
Emu.GetCallbacks().enable_disc_eject(true);
|
||||
Emu.GetCallbacks().enable_disc_eject(!!func_eject);
|
||||
Emu.GetCallbacks().enable_disc_insert(false);
|
||||
|
||||
return CELL_OK;
|
||||
@@ -423,13 +418,13 @@ error_code cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr<char> dirName
|
||||
const psf::registry psf = psf::load_object(fs::file(local_dir +"/PARAM.SFO"));
|
||||
|
||||
// Some following fields may be zero in old FW 1.00 version PARAM.SFO
|
||||
if (psf.contains("PARENTAL_LEVEL")) get->getParam.parentalLevel = psf.at("PARENTAL_LEVEL").as_integer();
|
||||
if (psf.contains("ATTRIBUTE")) get->getParam.attribute = psf.at("ATTRIBUTE").as_integer();
|
||||
if (psf.contains("RESOLUTION")) get->getParam.resolution = psf.at("RESOLUTION").as_integer();
|
||||
if (psf.contains("SOUND_FORMAT")) get->getParam.soundFormat = psf.at("SOUND_FORMAT").as_integer();
|
||||
if (psf.contains("TITLE")) strcpy_trunc(get->getParam.title, psf.at("TITLE").as_string());
|
||||
if (psf.contains("APP_VER")) strcpy_trunc(get->getParam.dataVersion, psf.at("APP_VER").as_string());
|
||||
if (psf.contains("TITLE_ID")) strcpy_trunc(get->getParam.titleId, psf.at("TITLE_ID").as_string());
|
||||
if (psf.contains("PARENTAL_LEVEL")) get->getParam.parentalLevel = ::at32(psf, "PARENTAL_LEVEL").as_integer();
|
||||
if (psf.contains("ATTRIBUTE")) get->getParam.attribute = ::at32(psf, "ATTRIBUTE").as_integer();
|
||||
if (psf.contains("RESOLUTION")) get->getParam.resolution = ::at32(psf, "RESOLUTION").as_integer();
|
||||
if (psf.contains("SOUND_FORMAT")) get->getParam.soundFormat = ::at32(psf, "SOUND_FORMAT").as_integer();
|
||||
if (psf.contains("TITLE")) strcpy_trunc(get->getParam.title, ::at32(psf, "TITLE").as_string());
|
||||
if (psf.contains("APP_VER")) strcpy_trunc(get->getParam.dataVersion, ::at32(psf, "APP_VER").as_string());
|
||||
if (psf.contains("TITLE_ID")) strcpy_trunc(get->getParam.titleId, ::at32(psf, "TITLE_ID").as_string());
|
||||
|
||||
for (u32 i = 0; i < CELL_HDDGAME_SYSP_LANGUAGE_NUM; i++)
|
||||
{
|
||||
@@ -1536,7 +1531,8 @@ error_code cellGameContentErrorDialog(s32 type, s32 errNeedSizeKB, vm::cptr<char
|
||||
return CELL_GAME_ERROR_PARAM;
|
||||
}
|
||||
|
||||
error_msg += "\n" + get_localized_string(localized_string_id::CELL_GAME_ERROR_DIR_NAME, fmt::format("%s", dirName).c_str());
|
||||
error_msg += '\n';
|
||||
error_msg += get_localized_string(localized_string_id::CELL_GAME_ERROR_DIR_NAME, fmt::format("%s", dirName).c_str());
|
||||
}
|
||||
|
||||
return open_exit_dialog(error_msg, type > CELL_GAME_ERRDIALOG_NOSPACE);
|
||||
@@ -1714,8 +1710,8 @@ error_code cellDiscGameGetBootDiscInfo(vm::ptr<CellDiscGameSystemFileParam> getP
|
||||
|
||||
const psf::registry psf = psf::load_object(fs::file(vfs::get(dir + "/PARAM.SFO")));
|
||||
|
||||
if (psf.contains("PARENTAL_LEVEL")) getParam->parentalLevel = psf.at("PARENTAL_LEVEL").as_integer();
|
||||
if (psf.contains("TITLE_ID")) strcpy_trunc(getParam->titleId, psf.at("TITLE_ID").as_string());
|
||||
if (psf.contains("PARENTAL_LEVEL")) getParam->parentalLevel = ::at32(psf, "PARENTAL_LEVEL").as_integer();
|
||||
if (psf.contains("TITLE_ID")) strcpy_trunc(getParam->titleId, ::at32(psf, "TITLE_ID").as_string());
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -322,7 +322,7 @@ error_code cellGcmBindZcull(u8 index, u32 offset, u32 width, u32 height, u32 cul
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
vm::_ptr<CellGcmZcullInfo>(gcm_cfg.zculls_addr)[index] = gcm_zcull;
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1314,8 +1314,6 @@ void _cellGcmSetFlipCommandWithWaitLabel(ppu_thread& ppu, vm::ptr<CellGcmContext
|
||||
{
|
||||
cellGcmSys.warning("cellGcmSetFlipCommandWithWaitLabel(ctx=*0x%x, id=0x%x, label_index=0x%x, label_value=0x%x)", ctx, id, label_index, label_value);
|
||||
|
||||
auto& gcm_cfg = g_fxo->get<gcm_config>();
|
||||
|
||||
rsx::make_command(ctx->current, NV406E_SEMAPHORE_OFFSET, { label_index * 0x10, label_value });
|
||||
|
||||
if (auto error = gcmSetPrepareFlip<true>(ppu, ctx, id); error < 0)
|
||||
|
||||
@@ -219,7 +219,7 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
gem_controller& controller = controllers.at(gem_num);
|
||||
gem_controller& controller = ::at32(controllers, gem_num);
|
||||
controller = {};
|
||||
controller.sphere_rgb = gem_color::get_default_color(gem_num);
|
||||
|
||||
@@ -446,7 +446,7 @@ static bool check_gem_num(const u32 gem_num)
|
||||
return gem_num < CELL_GEM_MAX_NUM;
|
||||
}
|
||||
|
||||
static inline void draw_overlay_cursor(u32 gem_num, const gem_config::gem_controller& controller, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max)
|
||||
static inline void draw_overlay_cursor(u32 gem_num, const gem_config::gem_controller&, s32 x_pos, s32 y_pos, s32 x_max, s32 y_max)
|
||||
{
|
||||
const u16 x = static_cast<u16>(x_pos / (x_max / static_cast<f32>(rsx::overlays::overlay::virtual_width)));
|
||||
const u16 y = static_cast<u16>(y_pos / (y_max / static_cast<f32>(rsx::overlays::overlay::virtual_height)));
|
||||
@@ -563,7 +563,7 @@ static void ds3_input_to_pad(const u32 port_no, be_t<u16>& digital_buttons, be_t
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pad = handler->GetPads().at(port_no);
|
||||
const auto& pad = ::at32(handler->GetPads(), port_no);
|
||||
|
||||
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
{
|
||||
@@ -656,7 +656,7 @@ static void ds3_pos_to_gem_image_state(const u32 port_no, const gem_config::gem_
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pad = handler->GetPads().at(port_no);
|
||||
const auto& pad = ::at32(handler->GetPads(), port_no);
|
||||
|
||||
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
{
|
||||
@@ -679,7 +679,7 @@ static void ds3_pos_to_gem_state(const u32 port_no, const gem_config::gem_contro
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pad = handler->GetPads().at(port_no);
|
||||
const auto& pad = ::at32(handler->GetPads(), port_no);
|
||||
|
||||
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
{
|
||||
@@ -712,7 +712,7 @@ static void ds3_input_to_ext(const u32 port_no, const gem_config::gem_controller
|
||||
std::lock_guard lock(pad::g_pad_mutex);
|
||||
|
||||
const auto handler = pad::get_current_handler();
|
||||
const auto& pad = handler->GetPads().at(port_no);
|
||||
const auto& pad = ::at32(handler->GetPads(), port_no);
|
||||
|
||||
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
|
||||
{
|
||||
@@ -769,7 +769,7 @@ static bool mouse_input_to_pad(const u32 mouse_no, be_t<u16>& digital_buttons, b
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& mouse_data = handler.GetMice().at(mouse_no);
|
||||
const auto& mouse_data = ::at32(handler.GetMice(), mouse_no);
|
||||
const auto is_pressed = [&mouse_data](MouseButtonCodes button) -> bool { return !!(mouse_data.buttons & button); };
|
||||
|
||||
digital_buttons = 0;
|
||||
@@ -822,7 +822,7 @@ static void mouse_pos_to_gem_image_state(const u32 mouse_no, const gem_config::g
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& mouse = handler.GetMice().at(mouse_no);
|
||||
const auto& mouse = ::at32(handler.GetMice(), mouse_no);
|
||||
|
||||
pos_to_gem_image_state(mouse_no, controller, gem_image_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max);
|
||||
}
|
||||
@@ -846,7 +846,7 @@ static void mouse_pos_to_gem_state(const u32 mouse_no, const gem_config::gem_con
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& mouse = handler.GetMice().at(mouse_no);
|
||||
const auto& mouse = ::at32(handler.GetMice(), mouse_no);
|
||||
|
||||
pos_to_gem_state(mouse_no, controller, gem_state, mouse.x_pos, mouse.y_pos, mouse.x_max, mouse.y_max);
|
||||
}
|
||||
|
||||
@@ -1120,7 +1120,7 @@ error_code cellHttpAuthCacheExport(vm::ptr<u32> buf, u32 len, vm::ptr<u32> outsi
|
||||
// TODO
|
||||
}
|
||||
|
||||
u32 size = 0;
|
||||
[[maybe_unused]] u32 size = 0;
|
||||
|
||||
// TODO
|
||||
|
||||
@@ -2786,7 +2786,7 @@ error_code cellHttpClientAddHeader(CellHttpClientId clientId, vm::cptr<CellHttpH
|
||||
error_code cellHttpClientDeleteHeader(CellHttpClientId clientId, vm::cptr<char> name)
|
||||
{
|
||||
cellHttp.todo("cellHttpClientDeleteHeader(clientId=0x%x, name=%s)", clientId, name);
|
||||
|
||||
|
||||
auto& man = g_fxo->get<http_manager>();
|
||||
std::lock_guard lock(man.mtx);
|
||||
|
||||
@@ -2806,7 +2806,7 @@ error_code cellHttpClientDeleteHeader(CellHttpClientId clientId, vm::cptr<char>
|
||||
error_code cellHttpClientSetSslCallback(CellHttpClientId clientId, vm::ptr<CellHttpsSslCallback> cbfunc, vm::ptr<void> userArg)
|
||||
{
|
||||
cellHttp.todo("cellHttpClientSetSslCallback(clientId=0x%x, cbfunc=*0x%x, userArg=*0x%x)", clientId, cbfunc, userArg);
|
||||
|
||||
|
||||
auto& man = g_fxo->get<http_manager>();
|
||||
std::lock_guard lock(man.mtx);
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ error_code cellHttpUtilBuildRequestLine(vm::cptr<CellHttpRequestLine> req, vm::p
|
||||
std::string path = fmt::format("%s", req->path);
|
||||
if (path.empty())
|
||||
{
|
||||
path = "/";
|
||||
path += '/';
|
||||
}
|
||||
|
||||
// TODO: are the numbers properly formatted ?
|
||||
@@ -805,7 +805,7 @@ error_code cellHttpUtilBase64Encoder(vm::ptr<char> out, vm::cptr<void> input, u3
|
||||
|
||||
error_code cellHttpUtilBase64Decoder(vm::ptr<char> output, vm::cptr<void> in, u32 len)
|
||||
{
|
||||
cellHttpUtil.todo("cellHttpUtilBase64Decoder(output=*0x%x, in=*0x%x, len=%d)", output, in, len);
|
||||
cellHttpUtil.todo("cellHttpUtilBase64Decoder(output=*0x%x, in=*0x%x, len=%d)", output, in, len);
|
||||
|
||||
if (!in)
|
||||
{
|
||||
|
||||
@@ -45,7 +45,14 @@ bool ime_jp_manager::addChar(u16 c)
|
||||
|
||||
std::u16string tmp;
|
||||
tmp += c;
|
||||
#if defined(__GNUG__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wrestrict"
|
||||
#endif
|
||||
input_string.insert(cursor++, tmp);
|
||||
#if defined(__GNUG__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
cursor_end = cursor;
|
||||
input_state = CELL_IMEJP_BEFORE_CONVERT;
|
||||
return true;
|
||||
|
||||
@@ -49,7 +49,7 @@ KeyboardHandlerBase::KeyboardHandlerBase(utils::serial* ar)
|
||||
Emu.DeferDeserialization([this]()
|
||||
{
|
||||
Init(m_info.max_connect);
|
||||
init.init();
|
||||
auto lk = init.init();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,5 +56,3 @@ struct CellKbConfig
|
||||
be_t<u32> read_mode;
|
||||
be_t<u32> code_type;
|
||||
};
|
||||
|
||||
u16 cellKbCnvRawCode(u32 arrange, u32 mkey, u32 led, u16 rawcode);
|
||||
|
||||
@@ -151,23 +151,23 @@ void mic_context::load_config_and_init()
|
||||
for (s32 index = 0; index < static_cast<s32>(device_list.size()); index++)
|
||||
{
|
||||
mic_list.emplace(std::piecewise_construct, std::forward_as_tuple(index), std::forward_as_tuple(microphone_handler::standard));
|
||||
mic_list.at(index).add_device(device_list[index]);
|
||||
::at32(mic_list, index).add_device(device_list[index]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case microphone_handler::singstar:
|
||||
{
|
||||
mic_list.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple(microphone_handler::singstar));
|
||||
mic_list.at(0).add_device(device_list[0]);
|
||||
::at32(mic_list, 0).add_device(device_list[0]);
|
||||
if (device_list.size() >= 2)
|
||||
mic_list.at(0).add_device(device_list[1]);
|
||||
::at32(mic_list, 0).add_device(device_list[1]);
|
||||
break;
|
||||
}
|
||||
case microphone_handler::real_singstar:
|
||||
case microphone_handler::rocksmith:
|
||||
{
|
||||
mic_list.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple(g_cfg.audio.microphone_type));
|
||||
mic_list.at(0).add_device(device_list[0]);
|
||||
::at32(mic_list, 0).add_device(device_list[0]);
|
||||
break;
|
||||
}
|
||||
case microphone_handler::null:
|
||||
@@ -563,7 +563,7 @@ error_code cellMicOpen(s32 dev_num, s32 sampleRate)
|
||||
if (!mic_thr.mic_list.count(dev_num))
|
||||
return CELL_MICIN_ERROR_DEVICE_NOT_FOUND;
|
||||
|
||||
auto& device = mic_thr.mic_list.at(dev_num);
|
||||
auto& device = ::at32(mic_thr.mic_list, dev_num);
|
||||
|
||||
if (device.is_opened())
|
||||
return CELL_MICIN_ERROR_ALREADY_OPEN;
|
||||
@@ -583,7 +583,7 @@ error_code cellMicOpenRaw(s32 dev_num, s32 sampleRate, s32 maxChannels)
|
||||
if (!mic_thr.mic_list.count(dev_num))
|
||||
return CELL_MICIN_ERROR_DEVICE_NOT_FOUND;
|
||||
|
||||
auto& device = mic_thr.mic_list.at(dev_num);
|
||||
auto& device = ::at32(mic_thr.mic_list, dev_num);
|
||||
|
||||
if (device.is_opened())
|
||||
return CELL_MICIN_ERROR_ALREADY_OPEN;
|
||||
@@ -604,7 +604,7 @@ error_code cellMicOpenEx(s32 dev_num, s32 rawSampleRate, s32 rawChannel, s32 DSP
|
||||
if (!mic_thr.mic_list.count(dev_num))
|
||||
return CELL_MICIN_ERROR_DEVICE_NOT_FOUND;
|
||||
|
||||
auto& device = mic_thr.mic_list.at(dev_num);
|
||||
auto& device = ::at32(mic_thr.mic_list, dev_num);
|
||||
|
||||
if (device.is_opened())
|
||||
return CELL_MICIN_ERROR_ALREADY_OPEN;
|
||||
@@ -626,7 +626,7 @@ u8 cellMicIsOpen(s32 dev_num)
|
||||
if (!mic_thr.mic_list.count(dev_num))
|
||||
return false;
|
||||
|
||||
return mic_thr.mic_list.at(dev_num).is_opened();
|
||||
return ::at32(mic_thr.mic_list, dev_num).is_opened();
|
||||
}
|
||||
|
||||
s32 cellMicIsAttached(s32 dev_num)
|
||||
@@ -647,7 +647,7 @@ error_code cellMicClose(s32 dev_num)
|
||||
if (!mic_thr.mic_list.count(dev_num))
|
||||
return CELL_MICIN_ERROR_DEVICE_NOT_FOUND;
|
||||
|
||||
auto& device = mic_thr.mic_list.at(dev_num);
|
||||
auto& device = ::at32(mic_thr.mic_list, dev_num);
|
||||
|
||||
if (!device.is_opened())
|
||||
return CELL_MICIN_ERROR_NOT_OPEN;
|
||||
@@ -669,7 +669,7 @@ error_code cellMicStart(s32 dev_num)
|
||||
if (!mic_thr.mic_list.count(dev_num))
|
||||
return CELL_MICIN_ERROR_DEVICE_NOT_FOUND;
|
||||
|
||||
auto& device = mic_thr.mic_list.at(dev_num);
|
||||
auto& device = ::at32(mic_thr.mic_list, dev_num);
|
||||
|
||||
if (!device.is_opened())
|
||||
return CELL_MICIN_ERROR_NOT_OPEN;
|
||||
@@ -691,7 +691,7 @@ error_code cellMicStartEx(s32 dev_num, u32 iflags)
|
||||
if (!mic_thr.mic_list.count(dev_num))
|
||||
return CELL_MICIN_ERROR_DEVICE_NOT_FOUND;
|
||||
|
||||
auto& device = mic_thr.mic_list.at(dev_num);
|
||||
auto& device = ::at32(mic_thr.mic_list, dev_num);
|
||||
|
||||
if (!device.is_opened())
|
||||
return CELL_MICIN_ERROR_NOT_OPEN;
|
||||
@@ -713,7 +713,7 @@ error_code cellMicStop(s32 dev_num)
|
||||
if (!mic_thr.mic_list.count(dev_num))
|
||||
return CELL_MICIN_ERROR_DEVICE_NOT_FOUND;
|
||||
|
||||
auto& device = mic_thr.mic_list.at(dev_num);
|
||||
auto& device = ::at32(mic_thr.mic_list, dev_num);
|
||||
|
||||
if (!device.is_opened())
|
||||
return CELL_MICIN_ERROR_NOT_OPEN;
|
||||
@@ -743,7 +743,7 @@ error_code cellMicGetDeviceAttr(s32 dev_num, CellMicDeviceAttr deviceAttributes,
|
||||
if (!mic_thr.mic_list.count(dev_num))
|
||||
return CELL_MICIN_ERROR_DEVICE_NOT_FOUND;
|
||||
|
||||
auto& device = mic_thr.mic_list.at(dev_num);
|
||||
auto& device = ::at32(mic_thr.mic_list, dev_num);
|
||||
|
||||
switch (deviceAttributes)
|
||||
{
|
||||
@@ -771,7 +771,7 @@ error_code cellMicSetDeviceAttr(s32 dev_num, CellMicDeviceAttr deviceAttributes,
|
||||
if (!mic_thr.mic_list.count(dev_num))
|
||||
return CELL_MICIN_ERROR_DEVICE_NOT_FOUND;
|
||||
|
||||
auto& device = mic_thr.mic_list.at(dev_num);
|
||||
auto& device = ::at32(mic_thr.mic_list, dev_num);
|
||||
|
||||
switch (deviceAttributes)
|
||||
{
|
||||
@@ -854,7 +854,7 @@ error_code cellMicGetFormatRaw(s32 dev_num, vm::ptr<CellMicInputFormatI> format)
|
||||
if (!mic_thr.mic_list.count(dev_num))
|
||||
return CELL_MICIN_ERROR_DEVICE_NOT_FOUND;
|
||||
|
||||
auto& device = mic_thr.mic_list.at(dev_num);
|
||||
auto& device = ::at32(mic_thr.mic_list, dev_num);
|
||||
|
||||
format->subframeSize = device.get_bit_resolution() / 8; // Probably?
|
||||
format->bitResolution = device.get_bit_resolution();
|
||||
@@ -956,7 +956,7 @@ error_code cellMicReadRaw(s32 dev_num, vm::ptr<void> data, s32 max_bytes)
|
||||
if (!mic_thr.mic_list.count(dev_num))
|
||||
return CELL_MICIN_ERROR_DEVICE_NOT_FOUND;
|
||||
|
||||
auto& mic = mic_thr.mic_list.at(dev_num);
|
||||
auto& mic = ::at32(mic_thr.mic_list, dev_num);
|
||||
|
||||
if (!mic.is_opened() || !(mic.get_signal_types() & CELLMIC_SIGTYPE_RAW))
|
||||
return CELL_MICIN_ERROR_NOT_OPEN;
|
||||
@@ -978,7 +978,7 @@ error_code cellMicRead(s32 dev_num, vm::ptr<void> data, u32 max_bytes)
|
||||
if (!mic_thr.mic_list.count(dev_num))
|
||||
return CELL_MICIN_ERROR_DEVICE_NOT_FOUND;
|
||||
|
||||
auto& mic = mic_thr.mic_list.at(dev_num);
|
||||
auto& mic = ::at32(mic_thr.mic_list, dev_num);
|
||||
|
||||
if (!mic.is_opened() || !(mic.get_signal_types() & CELLMIC_SIGTYPE_DSP))
|
||||
return CELL_MICIN_ERROR_NOT_OPEN;
|
||||
|
||||
@@ -49,7 +49,7 @@ MouseHandlerBase::MouseHandlerBase(utils::serial* ar)
|
||||
Emu.DeferDeserialization([this]()
|
||||
{
|
||||
Init(m_info.max_connect);
|
||||
init.init();
|
||||
auto lk = init.init();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ error_code open_msg_dialog(bool is_blocking, u32 type, vm::cptr<char> msgString,
|
||||
break;
|
||||
}
|
||||
|
||||
thread_ctrl::wait_on(ppu.state, state);
|
||||
ppu.state.wait(state);
|
||||
}
|
||||
|
||||
if (is_blocking)
|
||||
|
||||
@@ -161,8 +161,8 @@ struct music_state
|
||||
|
||||
if (next_track < playlist.size())
|
||||
{
|
||||
path = vfs::get(playlist.at(next_track));
|
||||
cellMusic.notice("set_playback_command: current vfs path: '%s' (unresolved='%s')", path, playlist.at(next_track));
|
||||
path = vfs::get(::at32(playlist, next_track));
|
||||
cellMusic.notice("set_playback_command: current vfs path: '%s' (unresolved='%s')", path, ::at32(playlist, next_track));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -244,10 +244,15 @@ error_code cellMusicGetSelectionContext(vm::ptr<CellMusicSelectionContext> conte
|
||||
|
||||
if (!context)
|
||||
return CELL_MUSIC_ERROR_PARAM;
|
||||
|
||||
|
||||
auto& music = g_fxo->get<music_state>();
|
||||
std::lock_guard lock(music.mtx);
|
||||
|
||||
if (!music.current_selection_context)
|
||||
{
|
||||
return CELL_MUSIC2_ERROR_NO_ACTIVE_CONTENT;
|
||||
}
|
||||
|
||||
*context = music.current_selection_context.get();
|
||||
cellMusic.success("cellMusicGetSelectionContext: selection context = %s", music.current_selection_context.to_string());
|
||||
|
||||
@@ -317,6 +322,12 @@ error_code cellMusicGetContentsId(vm::ptr<CellSearchContentId> contents_id)
|
||||
// HACKY
|
||||
auto& music = g_fxo->get<music_state>();
|
||||
std::lock_guard lock(music.mtx);
|
||||
|
||||
if (!music.current_selection_context)
|
||||
{
|
||||
return CELL_MUSIC2_ERROR_NO_ACTIVE_CONTENT;
|
||||
}
|
||||
|
||||
return music.current_selection_context.find_content_id(contents_id);
|
||||
}
|
||||
|
||||
@@ -363,6 +374,7 @@ error_code cellMusicInitialize2SystemWorkload(s32 mode, vm::ptr<CellMusic2Callba
|
||||
auto& music = g_fxo->get<music_state>();
|
||||
music.func = func;
|
||||
music.userData = userData;
|
||||
music.current_selection_context = {};
|
||||
|
||||
sysutil_register_cb([=, &music](ppu_thread& ppu) -> s32
|
||||
{
|
||||
@@ -429,6 +441,7 @@ error_code cellMusicInitializeSystemWorkload(s32 mode, u32 container, vm::ptr<Ce
|
||||
auto& music = g_fxo->get<music_state>();
|
||||
music.func = func;
|
||||
music.userData = userData;
|
||||
music.current_selection_context = {};
|
||||
|
||||
sysutil_register_cb([=, &music](ppu_thread& ppu) -> s32
|
||||
{
|
||||
@@ -451,6 +464,7 @@ error_code cellMusicInitialize(s32 mode, u32 container, s32 spuPriority, vm::ptr
|
||||
auto& music = g_fxo->get<music_state>();
|
||||
music.func = func;
|
||||
music.userData = userData;
|
||||
music.current_selection_context = {};
|
||||
|
||||
sysutil_register_cb([=, &music](ppu_thread& ppu) -> s32
|
||||
{
|
||||
@@ -489,6 +503,11 @@ error_code cellMusicGetSelectionContext2(vm::ptr<CellMusicSelectionContext> cont
|
||||
auto& music = g_fxo->get<music_state>();
|
||||
std::lock_guard lock(music.mtx);
|
||||
|
||||
if (!music.current_selection_context)
|
||||
{
|
||||
return CELL_MUSIC2_ERROR_NO_ACTIVE_CONTENT;
|
||||
}
|
||||
|
||||
*context = music.current_selection_context.get();
|
||||
cellMusic.success("cellMusicGetSelectionContext2: selection context = %s", music.current_selection_context.to_string());
|
||||
|
||||
@@ -533,14 +552,21 @@ error_code cellMusicSetPlaybackCommand2(s32 command, vm::ptr<void> param)
|
||||
if (!music.func)
|
||||
return CELL_MUSIC2_ERROR_GENERIC;
|
||||
|
||||
sysutil_register_cb([=, &music](ppu_thread& ppu) -> s32
|
||||
error_code result = CELL_OK;
|
||||
|
||||
if (!music.current_selection_context)
|
||||
{
|
||||
const error_code result = music.set_playback_command(command);
|
||||
result = CELL_MUSIC_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
sysutil_register_cb([=, &music, prev_res = result](ppu_thread& ppu) -> s32
|
||||
{
|
||||
const error_code result = prev_res ? prev_res : music.set_playback_command(command);
|
||||
music.func(ppu, CELL_MUSIC2_EVENT_SET_PLAYBACK_COMMAND_RESULT, vm::addr_t(+result), music.userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
return result;
|
||||
}
|
||||
|
||||
error_code cellMusicSetPlaybackCommand(s32 command, vm::ptr<void> param)
|
||||
@@ -555,14 +581,21 @@ error_code cellMusicSetPlaybackCommand(s32 command, vm::ptr<void> param)
|
||||
if (!music.func)
|
||||
return CELL_MUSIC_ERROR_GENERIC;
|
||||
|
||||
sysutil_register_cb([=, &music](ppu_thread& ppu) -> s32
|
||||
error_code result = CELL_OK;
|
||||
|
||||
if (!music.current_selection_context)
|
||||
{
|
||||
const error_code result = music.set_playback_command(command);
|
||||
result = CELL_MUSIC2_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
sysutil_register_cb([=, &music, prev_res = result](ppu_thread& ppu) -> s32
|
||||
{
|
||||
const error_code result = prev_res ? prev_res : music.set_playback_command(command);
|
||||
music.func(ppu, CELL_MUSIC_EVENT_SET_PLAYBACK_COMMAND_RESULT, vm::addr_t(+result), music.userData);
|
||||
return CELL_OK;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
return result;
|
||||
}
|
||||
|
||||
error_code cellMusicSelectContents2()
|
||||
@@ -591,6 +624,7 @@ error_code cellMusicInitialize2(s32 mode, s32 spuPriority, vm::ptr<CellMusic2Cal
|
||||
auto& music = g_fxo->get<music_state>();
|
||||
music.func = func;
|
||||
music.userData = userData;
|
||||
music.current_selection_context = {};
|
||||
|
||||
sysutil_register_cb([=, &music](ppu_thread& ppu) -> s32
|
||||
{
|
||||
|
||||
@@ -141,6 +141,7 @@ struct CellMusicSelectionContext
|
||||
|
||||
struct music_selection_context
|
||||
{
|
||||
bool valid = false;
|
||||
char magic[4] = "SUS";
|
||||
std::string hash;
|
||||
CellSearchContentType content_type = CELL_SEARCH_CONTENTTYPE_MUSIC;
|
||||
@@ -167,6 +168,11 @@ struct music_selection_context
|
||||
bool load_playlist();
|
||||
u32 step_track(bool next);
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return atomic_storage<bool>::load(valid);
|
||||
}
|
||||
|
||||
// Helper
|
||||
error_code find_content_id(vm::ptr<CellSearchContentId> contents_id);
|
||||
};
|
||||
|
||||
@@ -235,7 +235,7 @@ error_code cell_music_decode_read(vm::ptr<void> buf, vm::ptr<u32> startTime, u64
|
||||
{
|
||||
start_time_ms = dec.decoder.timestamps_ms.front().second;
|
||||
|
||||
while (dec.decoder.timestamps_ms.size() > 1 && dec.read_pos >= dec.decoder.timestamps_ms.at(1).first)
|
||||
while (dec.decoder.timestamps_ms.size() > 1 && dec.read_pos >= ::at32(dec.decoder.timestamps_ms, 1).first)
|
||||
{
|
||||
dec.decoder.timestamps_ms.pop_front();
|
||||
}
|
||||
@@ -274,7 +274,7 @@ error_code cell_music_decode_read(vm::ptr<void> buf, vm::ptr<u32> startTime, u64
|
||||
error_code cellMusicDecodeInitialize(s32 mode, u32 container, s32 spuPriority, vm::ptr<CellMusicDecodeCallback> func, vm::ptr<void> userData)
|
||||
{
|
||||
cellMusicDecode.warning("cellMusicDecodeInitialize(mode=0x%x, container=0x%x, spuPriority=0x%x, func=*0x%x, userData=*0x%x)", mode, container, spuPriority, func, userData);
|
||||
|
||||
|
||||
if (mode != CELL_MUSIC_DECODE2_MODE_NORMAL || (spuPriority - 0x10U > 0xef) || !func)
|
||||
{
|
||||
return CELL_MUSIC_DECODE_ERROR_PARAM;
|
||||
|
||||
@@ -47,7 +47,7 @@ std::string music_selection_context::to_string() const
|
||||
|
||||
for (usz i = 0; i < playlist.size(); i++)
|
||||
{
|
||||
fmt::append(str, "\n - Track %d: %s", i, playlist.at(i));
|
||||
fmt::append(str, "\n - Track %d: %s", i, ::at32(playlist, i));
|
||||
}
|
||||
|
||||
return str;
|
||||
@@ -242,6 +242,7 @@ bool music_selection_context::load_playlist()
|
||||
playlist.push_back(track_node[i].Scalar());
|
||||
}
|
||||
|
||||
valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ error_code cellNetCtlGetInfo(s32 code, vm::ptr<CellNetCtlInfo> info)
|
||||
case CELL_NET_CTL_INFO_DEFAULT_ROUTE: strcpy_trunc(info->default_route, "192.168.1.1"); break;
|
||||
case CELL_NET_CTL_INFO_PRIMARY_DNS: strcpy_trunc(info->primary_dns, np::ip_to_string(nph.get_dns_ip())); break;
|
||||
case CELL_NET_CTL_INFO_SECONDARY_DNS: strcpy_trunc(info->secondary_dns, np::ip_to_string(nph.get_dns_ip())); break;
|
||||
case CELL_NET_CTL_INFO_IP_ADDRESS: strcpy_trunc(info->ip_address, np::ip_to_string(nph.get_local_ip_addr())); break;
|
||||
case CELL_NET_CTL_INFO_IP_ADDRESS: strcpy_trunc(info->ip_address, np::ip_to_string(nph.get_public_ip_addr())); break;
|
||||
case CELL_NET_CTL_INFO_NETMASK: strcpy_trunc(info->netmask, "255.255.255.255"); break;
|
||||
case CELL_NET_CTL_INFO_HTTP_PROXY_CONFIG: info->http_proxy_config = 0; break;
|
||||
case CELL_NET_CTL_INFO_DHCP_HOSTNAME: strcpy_trunc(info->dhcp_hostname, nph.get_hostname()); break;
|
||||
@@ -237,7 +237,7 @@ struct netstart_hack
|
||||
|
||||
error_code cellNetCtlNetStartDialogLoadAsync(vm::cptr<CellNetCtlNetStartDialogParam> param)
|
||||
{
|
||||
cellNetCtl.error("cellNetCtlNetStartDialogLoadAsync(param=*0x%x)", param);
|
||||
cellNetCtl.warning("cellNetCtlNetStartDialogLoadAsync(param=*0x%x)", param);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ bool check_config(vm::cptr<CellPngEncConfig> config)
|
||||
if (!config ||
|
||||
config->maxWidth == 0u || config->maxWidth > 1000000u ||
|
||||
config->maxHeight == 0u || config->maxHeight > 1000000u ||
|
||||
config->maxBitDepth != 8u && config->maxBitDepth != 16u ||
|
||||
(config->maxBitDepth != 8u && config->maxBitDepth != 16u) ||
|
||||
static_cast<s32>(config->addMemSize) < 0 ||
|
||||
config->exParamNum != 0u)
|
||||
{
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
#include "Emu/Cell/lv2/sys_memory.h"
|
||||
#include "Emu/Cell/lv2/sys_ss.h"
|
||||
|
||||
//#include <iomanip>
|
||||
//#include <sstream>
|
||||
|
||||
LOG_CHANNEL(cellRtc);
|
||||
|
||||
// clang-format off
|
||||
@@ -43,16 +40,16 @@ void fmt_class_string<CellRtcError>::format(std::string& out, u64 arg)
|
||||
|
||||
// Grabbed from JPCSP
|
||||
// This is the # of microseconds between January 1, 0001 and January 1, 1970.
|
||||
const u64 RTC_MAGIC_OFFSET = 62135596800000000ULL;
|
||||
constexpr u64 RTC_MAGIC_OFFSET = 62135596800000000ULL;
|
||||
// This is the # of microseconds between January 1, 0001 and January 1, 1601 (for Win32 FILETIME.)
|
||||
const u64 RTC_FILETIME_OFFSET = 50491123200000000ULL;
|
||||
constexpr u64 RTC_FILETIME_OFFSET = 50491123200000000ULL;
|
||||
|
||||
const u64 EPOCH_AS_FILETIME = 116444736000000000ULL;
|
||||
constexpr u64 EPOCH_AS_FILETIME = 116444736000000000ULL;
|
||||
|
||||
// Also stores leap year
|
||||
const u8 DAYS_IN_MONTH[24] = {0x1F, 0x1C, 0x1F, 0x1E, 0x1F, 0x1E, 0x1F, 0x1F, 0x1E, 0x1F, 0x1E, 0x1F, 0x1F, 0x1D, 0x1F, 0x1E, 0x1F, 0x1E, 0x1F, 0x1F, 0x1E, 0x1F, 0x1E, 0x1F};
|
||||
const char WEEKDAY_NAMES[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; // 4 as terminator
|
||||
const char MONTH_NAMES[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; // 4 as terminator
|
||||
constexpr u8 DAYS_IN_MONTH[24] = {0x1F, 0x1C, 0x1F, 0x1E, 0x1F, 0x1E, 0x1F, 0x1F, 0x1E, 0x1F, 0x1E, 0x1F, 0x1F, 0x1D, 0x1F, 0x1E, 0x1F, 0x1E, 0x1F, 0x1F, 0x1E, 0x1F, 0x1E, 0x1F};
|
||||
constexpr char WEEKDAY_NAMES[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; // 4 as terminator
|
||||
constexpr char MONTH_NAMES[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; // 4 as terminator
|
||||
|
||||
s64 convertToUNIXTime(u16 seconds, u16 minutes, u16 hours, u16 days, s32 years)
|
||||
{
|
||||
@@ -122,7 +119,7 @@ error_code cellRtcGetCurrentTick(vm::ptr<CellRtcTick> pTick)
|
||||
|
||||
error_code cellRtcGetCurrentClock(vm::ptr<CellRtcDateTime> pClock, s32 iTimeZone)
|
||||
{
|
||||
cellRtc.todo("cellRtcGetCurrentClock(pClock=*0x%x, time_zone=%d)", pClock, iTimeZone);
|
||||
cellRtc.todo("cellRtcGetCurrentClock(pClock=*0x%x, iTimeZone=%d)", pClock, iTimeZone);
|
||||
|
||||
if (!vm::check_addr(pClock.addr()))
|
||||
{
|
||||
@@ -1131,6 +1128,13 @@ error_code cellRtcGetCurrentSecureTick(vm::ptr<CellRtcTick> tick)
|
||||
{
|
||||
cellRtc.todo("cellRtcGetCurrentSecureTick(*0x%x)", tick);
|
||||
|
||||
if (!vm::check_addr(tick.addr()))
|
||||
{
|
||||
return CELL_RTC_ERROR_INVALID_POINTER;
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1445,7 +1449,7 @@ error_code cellRtcIsLeapYear(s32 year)
|
||||
{
|
||||
cellRtc.todo("cellRtcIsLeapYear(year=%d)", year);
|
||||
|
||||
if (year < 0)
|
||||
if (year < 1)
|
||||
{
|
||||
return CELL_RTC_ERROR_INVALID_ARG;
|
||||
}
|
||||
@@ -1457,7 +1461,7 @@ error_code cellRtcGetDaysInMonth(s32 year, s32 month)
|
||||
{
|
||||
cellRtc.todo("cellRtcGetDaysInMonth(year=%d, month=%d)", year, month);
|
||||
|
||||
if ((year < 0) || (month <= 0) || (month > 12))
|
||||
if ((year <= 0) || (month <= 0) || (month > 12))
|
||||
{
|
||||
return CELL_RTC_ERROR_INVALID_ARG;
|
||||
}
|
||||
@@ -1474,10 +1478,10 @@ error_code cellRtcGetDayOfWeek(s32 year, s32 month, s32 day)
|
||||
{
|
||||
cellRtc.trace("cellRtcGetDayOfWeek(year=%d, month=%d, day=%d)", year, month, day);
|
||||
|
||||
if (month - 1 < 2)
|
||||
if (month == 1 || month == 2)
|
||||
{
|
||||
year -= 1;
|
||||
month += 0xc;
|
||||
year--;
|
||||
month += 12;
|
||||
}
|
||||
|
||||
return not_an_error(((month * 0xd + 8) / 5 + ((year + (year >> 2) + (year < 0 && (year & 3U) != 0)) - year / 100) + year / 400 + day) % 7);
|
||||
|
||||
@@ -29,13 +29,13 @@ struct CellRtcTick
|
||||
|
||||
struct CellRtcDateTime
|
||||
{
|
||||
be_t<u16> year;
|
||||
be_t<u16> month;
|
||||
be_t<u16> day;
|
||||
be_t<u16> hour;
|
||||
be_t<u16> minute;
|
||||
be_t<u16> second;
|
||||
be_t<u32> microsecond;
|
||||
be_t<u16> year; // 1 to 9999
|
||||
be_t<u16> month; // 1 to 12
|
||||
be_t<u16> day; // 1 to 31
|
||||
be_t<u16> hour; // 0 to 23
|
||||
be_t<u16> minute; // 0 to 59
|
||||
be_t<u16> second; // 0 to 59
|
||||
be_t<u32> microsecond; // 0 to 999999
|
||||
};
|
||||
|
||||
error_code cellRtcTickAddYears(vm::ptr<CellRtcTick> pTick0, vm::cptr<CellRtcTick> pTick1, s32 iAdd);
|
||||
|
||||
@@ -148,7 +148,7 @@ int check_filename(std::string_view file_path, bool disallow_system_files, bool
|
||||
return 70;
|
||||
}
|
||||
|
||||
char name[CELL_SAVEDATA_FILENAME_SIZE - 3];
|
||||
char name[CELL_SAVEDATA_FILENAME_SIZE + 3];
|
||||
|
||||
if (dotpos)
|
||||
{
|
||||
|
||||
@@ -1297,7 +1297,7 @@ error_code cellSearchStartSceneSearch(CellSearchSceneSearchType searchType, vm::
|
||||
return CELL_SEARCH_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (tagNum) // TODO: find out if this is the correct location for these checks
|
||||
if (tagNum) // TODO: find out if this is the correct location for these checks
|
||||
{
|
||||
if (tagNum > CELL_SEARCH_TAG_NUM_MAX || !tags)
|
||||
{
|
||||
@@ -1580,7 +1580,7 @@ error_code cellSearchGetContentIdByOffset(CellSearchId searchId, s32 offset, vm:
|
||||
|
||||
if (offset >= 0 && offset + 0u < searchObject->content_ids.size())
|
||||
{
|
||||
auto& content_id = searchObject->content_ids.at(offset);
|
||||
auto& content_id = ::at32(searchObject->content_ids, offset);
|
||||
const u128 content_id_128 = content_id.first;
|
||||
*outContentType = content_id.second->type;
|
||||
std::memcpy(outContentId->data, &content_id_128, CELL_SEARCH_CONTENT_ID_SIZE);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/IdManager.h"
|
||||
|
||||
#include "cellSubDisplay.h"
|
||||
|
||||
@@ -27,15 +28,147 @@ void fmt_class_string<CellSubDisplayError>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
enum class sub_display_status : u32
|
||||
{
|
||||
uninitialized = 0,
|
||||
stopped = 1,
|
||||
started = 2
|
||||
};
|
||||
|
||||
struct sub_display_manager
|
||||
{
|
||||
shared_mutex mutex;
|
||||
sub_display_status status = sub_display_status::uninitialized;
|
||||
CellSubDisplayParam param{};
|
||||
vm::ptr<CellSubDisplayHandler> func = vm::null;
|
||||
vm::ptr<void> userdata = vm::null;
|
||||
|
||||
// Video data
|
||||
vm::ptr<void> video_buffer = vm::null;
|
||||
u32 buf_size = 0;
|
||||
|
||||
// Audio data
|
||||
bool audio_is_busy = false;
|
||||
|
||||
// Touch data
|
||||
std::array<CellSubDisplayTouchInfo, CELL_SUBDISPLAY_TOUCH_MAX_TOUCH_INFO> touch_info{};
|
||||
};
|
||||
|
||||
|
||||
error_code check_param(CellSubDisplayParam* param)
|
||||
{
|
||||
if (!param ||
|
||||
param->version < CELL_SUBDISPLAY_VERSION_0001 ||
|
||||
param->version > CELL_SUBDISPLAY_VERSION_0003 ||
|
||||
param->mode != CELL_SUBDISPLAY_MODE_REMOTEPLAY ||
|
||||
param->nGroup != 1 ||
|
||||
param->nPeer != 1 ||
|
||||
param->audioParam.ch != 2 ||
|
||||
param->audioParam.audioMode < CELL_SUBDISPLAY_AUDIO_MODE_SETDATA ||
|
||||
param->audioParam.audioMode > CELL_SUBDISPLAY_AUDIO_MODE_CAPTURE)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
switch (param->videoParam.format)
|
||||
{
|
||||
case CELL_SUBDISPLAY_VIDEO_FORMAT_A8R8G8B8:
|
||||
case CELL_SUBDISPLAY_VIDEO_FORMAT_R8G8B8A8:
|
||||
{
|
||||
if (param->version == CELL_SUBDISPLAY_VERSION_0003 ||
|
||||
param->videoParam.width != 480 ||
|
||||
param->videoParam.height != 272 ||
|
||||
(param->videoParam.pitch != 1920 && param->videoParam.pitch != 2048) ||
|
||||
param->videoParam.aspectRatio < CELL_SUBDISPLAY_VIDEO_ASPECT_RATIO_16_9 ||
|
||||
param->videoParam.aspectRatio > CELL_SUBDISPLAY_VIDEO_ASPECT_RATIO_4_3 ||
|
||||
param->videoParam.videoMode < CELL_SUBDISPLAY_VIDEO_MODE_SETDATA ||
|
||||
param->videoParam.videoMode > CELL_SUBDISPLAY_VIDEO_MODE_CAPTURE)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case CELL_SUBDISPLAY_VIDEO_FORMAT_YUV420:
|
||||
{
|
||||
if (param->version != CELL_SUBDISPLAY_VERSION_0003 ||
|
||||
param->videoParam.width != CELL_SUBDISPLAY_0003_WIDTH ||
|
||||
param->videoParam.height != CELL_SUBDISPLAY_0003_HEIGHT ||
|
||||
param->videoParam.pitch != CELL_SUBDISPLAY_0003_PITCH ||
|
||||
param->videoParam.aspectRatio != CELL_SUBDISPLAY_VIDEO_ASPECT_RATIO_16_9 ||
|
||||
param->videoParam.videoMode != CELL_SUBDISPLAY_VIDEO_MODE_SETDATA)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_INVALID_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSubDisplayInit(vm::ptr<CellSubDisplayParam> pParam, vm::ptr<CellSubDisplayHandler> func, vm::ptr<void> userdata, u32 container)
|
||||
{
|
||||
cellSubDisplay.todo("cellSubDisplayInit(pParam=*0x%x, func=*0x%x, userdata=*0x%x, container=0x%x)", pParam, func, userdata, container);
|
||||
return CELL_SUBDISPLAY_ERROR_ZERO_REGISTERED;
|
||||
|
||||
auto& manager = g_fxo->get<sub_display_manager>();
|
||||
std::lock_guard lock(manager.mutex);
|
||||
|
||||
if (manager.func)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (error_code error = check_param(pParam.get_ptr()))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!func)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
manager.param = *pParam;
|
||||
manager.func = func;
|
||||
manager.userdata = userdata;
|
||||
|
||||
if (true) // TODO
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_ZERO_REGISTERED;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSubDisplayEnd()
|
||||
{
|
||||
cellSubDisplay.todo("cellSubDisplayEnd()");
|
||||
|
||||
auto& manager = g_fxo->get<sub_display_manager>();
|
||||
std::lock_guard lock(manager.mutex);
|
||||
|
||||
if (!manager.func)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (manager.status == sub_display_status::started)
|
||||
{
|
||||
// TODO:
|
||||
// cellSubDisplayStop();
|
||||
}
|
||||
|
||||
manager.param = {};
|
||||
manager.func = vm::null;
|
||||
manager.userdata = vm::null;
|
||||
manager.status = sub_display_status::uninitialized;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -43,6 +176,11 @@ error_code cellSubDisplayGetRequiredMemory(vm::ptr<CellSubDisplayParam> pParam)
|
||||
{
|
||||
cellSubDisplay.warning("cellSubDisplayGetRequiredMemory(pParam=*0x%x)", pParam);
|
||||
|
||||
if (error_code error = check_param(pParam.get_ptr()))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
switch (pParam->version)
|
||||
{
|
||||
case CELL_SUBDISPLAY_VERSION_0001: return not_an_error(CELL_SUBDISPLAY_0001_MEMORY_CONTAINER_SIZE);
|
||||
@@ -57,18 +195,61 @@ error_code cellSubDisplayGetRequiredMemory(vm::ptr<CellSubDisplayParam> pParam)
|
||||
error_code cellSubDisplayStart()
|
||||
{
|
||||
cellSubDisplay.todo("cellSubDisplayStart()");
|
||||
|
||||
auto& manager = g_fxo->get<sub_display_manager>();
|
||||
std::lock_guard lock(manager.mutex);
|
||||
|
||||
if (manager.status == sub_display_status::uninitialized)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
manager.status = sub_display_status::started;
|
||||
|
||||
// TODO
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSubDisplayStop()
|
||||
{
|
||||
cellSubDisplay.todo("cellSubDisplayStop()");
|
||||
|
||||
auto& manager = g_fxo->get<sub_display_manager>();
|
||||
std::lock_guard lock(manager.mutex);
|
||||
|
||||
if (manager.status == sub_display_status::uninitialized)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
manager.status = sub_display_status::stopped;
|
||||
|
||||
// TODO
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSubDisplayGetVideoBuffer(s32 groupId, vm::pptr<void> ppVideoBuf, vm::ptr<u32> pSize)
|
||||
{
|
||||
cellSubDisplay.todo("cellSubDisplayGetVideoBuffer(groupId=%d, ppVideoBuf=**0x%x, pSize=*0x%x)", groupId, ppVideoBuf, pSize);
|
||||
|
||||
auto& manager = g_fxo->get<sub_display_manager>();
|
||||
std::lock_guard lock(manager.mutex);
|
||||
|
||||
if (manager.status == sub_display_status::uninitialized)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (groupId != 0 || !ppVideoBuf || !pSize)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
*pSize = manager.buf_size;
|
||||
*ppVideoBuf = manager.video_buffer;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -76,11 +257,26 @@ error_code cellSubDisplayAudioOutBlocking(s32 groupId, vm::ptr<void> pvData, s32
|
||||
{
|
||||
cellSubDisplay.todo("cellSubDisplayAudioOutBlocking(groupId=%d, pvData=*0x%x, samples=%d)", groupId, pvData, samples);
|
||||
|
||||
auto& manager = g_fxo->get<sub_display_manager>();
|
||||
std::lock_guard lock(manager.mutex);
|
||||
|
||||
if (manager.status == sub_display_status::uninitialized)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (groupId != 0 || samples < 0)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
if (samples % 1024)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_SET_SAMPLE;
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -88,32 +284,110 @@ error_code cellSubDisplayAudioOutNonBlocking(s32 groupId, vm::ptr<void> pvData,
|
||||
{
|
||||
cellSubDisplay.todo("cellSubDisplayAudioOutNonBlocking(groupId=%d, pvData=*0x%x, samples=%d)", groupId, pvData, samples);
|
||||
|
||||
auto& manager = g_fxo->get<sub_display_manager>();
|
||||
std::lock_guard lock(manager.mutex);
|
||||
|
||||
if (manager.status == sub_display_status::uninitialized)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (groupId != 0 || samples < 0)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
if (samples % 1024)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_SET_SAMPLE;
|
||||
}
|
||||
|
||||
if (manager.audio_is_busy)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_AUDIOOUT_IS_BUSY;
|
||||
}
|
||||
|
||||
// TODO: fetch audio async
|
||||
// manager.audio_is_busy = true;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSubDisplayGetPeerNum(s32 groupId)
|
||||
{
|
||||
cellSubDisplay.todo("cellSubDisplayGetPeerNum(groupId=%d)", groupId);
|
||||
return CELL_OK;
|
||||
|
||||
auto& manager = g_fxo->get<sub_display_manager>();
|
||||
std::lock_guard lock(manager.mutex);
|
||||
|
||||
if (manager.status == sub_display_status::uninitialized)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (groupId != 0)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
s32 peer_num = 0; // TODO
|
||||
|
||||
return not_an_error(peer_num);
|
||||
}
|
||||
|
||||
error_code cellSubDisplayGetPeerList(s32 groupId, vm::ptr<CellSubDisplayPeerInfo> pInfo, vm::ptr<s32> pNum)
|
||||
{
|
||||
cellSubDisplay.todo("cellSubDisplayGetPeerList(groupId=%d, pInfo=*0x%x, pNum=*0x%x)", groupId, pInfo, pNum);
|
||||
|
||||
auto& manager = g_fxo->get<sub_display_manager>();
|
||||
std::lock_guard lock(manager.mutex);
|
||||
|
||||
if (manager.status == sub_display_status::uninitialized)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (groupId != 0)
|
||||
{
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
if (!pInfo || !pNum || *pNum < 1)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
*pNum = 0;
|
||||
|
||||
// TODO
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSubDisplayGetTouchInfo(s32 groupId, vm::ptr<CellSubDisplayTouchInfo> pTouchInfo, vm::ptr<s32> pNumTouchInfo)
|
||||
{
|
||||
cellSubDisplay.todo("cellSubDisplayGetTouchInfo(groupId=%d, pTouchInfo=*0x%x, pNumTouchInfo=*0x%x)", groupId, pTouchInfo, pNumTouchInfo);
|
||||
|
||||
if (groupId != 0 || !pNumTouchInfo || !pTouchInfo)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
auto& manager = g_fxo->get<sub_display_manager>();
|
||||
std::lock_guard lock(manager.mutex);
|
||||
|
||||
if (manager.param.version != CELL_SUBDISPLAY_VERSION_0003)
|
||||
{
|
||||
return CELL_SUBDISPLAY_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (*pNumTouchInfo > CELL_SUBDISPLAY_TOUCH_MAX_TOUCH_INFO)
|
||||
{
|
||||
*pNumTouchInfo = CELL_SUBDISPLAY_TOUCH_MAX_TOUCH_INFO;
|
||||
}
|
||||
|
||||
std::memcpy(pTouchInfo.get_ptr(), manager.touch_info.data(), *pNumTouchInfo * sizeof(CellSubDisplayTouchInfo));
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,25 +20,34 @@ enum
|
||||
CELL_SUBDISPLAY_STATUS_JOIN = 1,
|
||||
CELL_SUBDISPLAY_STATUS_LEAVE = 2,
|
||||
CELL_SUBDISPLAY_STATUS_FATALERROR = 3,
|
||||
|
||||
CELL_SUBDISPLAY_VERSION_0001 = 1,
|
||||
CELL_SUBDISPLAY_VERSION_0002 = 2,
|
||||
CELL_SUBDISPLAY_VERSION_0003 = 3,
|
||||
|
||||
CELL_SUBDISPLAY_MODE_REMOTEPLAY = 1,
|
||||
|
||||
CELL_SUBDISPLAY_VIDEO_FORMAT_A8R8G8B8 = 1,
|
||||
CELL_SUBDISPLAY_VIDEO_FORMAT_R8G8B8A8 = 2,
|
||||
CELL_SUBDISPLAY_VIDEO_FORMAT_YUV420 = 3,
|
||||
|
||||
CELL_SUBDISPLAY_VIDEO_ASPECT_RATIO_16_9 = 0,
|
||||
CELL_SUBDISPLAY_VIDEO_ASPECT_RATIO_4_3 = 1,
|
||||
|
||||
CELL_SUBDISPLAY_VIDEO_MODE_SETDATA = 0,
|
||||
CELL_SUBDISPLAY_VIDEO_MODE_CAPTURE = 1,
|
||||
|
||||
CELL_SUBDISPLAY_AUDIO_MODE_SETDATA = 0,
|
||||
CELL_SUBDISPLAY_AUDIO_MODE_CAPTURE = 1,
|
||||
|
||||
CELL_SUBDISPLAY_0001_MEMORY_CONTAINER_SIZE = 8 * 1024 * 1024,
|
||||
CELL_SUBDISPLAY_0002_MEMORY_CONTAINER_SIZE = 10 * 1024 * 1024,
|
||||
CELL_SUBDISPLAY_0003_MEMORY_CONTAINER_SIZE = 10 * 1024 * 1024,
|
||||
|
||||
CELL_SUBDISPLAY_0003_WIDTH = 864,
|
||||
CELL_SUBDISPLAY_0003_PITCH = 864,
|
||||
CELL_SUBDISPLAY_0003_HEIGHT = 480,
|
||||
|
||||
CELL_SUBDISPLAY_NICKNAME_LEN = 256,
|
||||
CELL_SUBDISPLAY_PSPID_LEN = 16,
|
||||
};
|
||||
|
||||
@@ -315,7 +315,7 @@ error_code cellSysutilAvc2EstimateMemoryContainerSize(vm::cptr<CellSysutilAvc2In
|
||||
val += static_cast<s32>(static_cast<f64>(window_count) * 1258291.2) + 0x1ed846;
|
||||
}
|
||||
|
||||
estimated_size = (estimated_size + ((static_cast<int>(val) >> 7) + static_cast<u32>(static_cast<int>(val) < 0 && (val & 0x7f) != 0)) * 0x80 + 0x80080 & 0xfff00000) + 0x100000;
|
||||
estimated_size = ((estimated_size + ((static_cast<int>(val) >> 7) + static_cast<u32>(static_cast<int>(val) < 0 && (val & 0x7f) != 0)) * 0x80 + 0x80080) & 0xfff00000) + 0x100000;
|
||||
|
||||
*size = estimated_size;
|
||||
}
|
||||
|
||||
@@ -563,9 +563,10 @@ struct vdec_context final
|
||||
break;
|
||||
}
|
||||
}
|
||||
thread_ctrl::wait_for(1000);
|
||||
|
||||
if (elapsed++ >= 5000) // 5 seconds
|
||||
thread_ctrl::wait_for(10000);
|
||||
|
||||
if (elapsed++ >= 500) // 5 seconds
|
||||
{
|
||||
cellVdec.error("Video au decode has been waiting for a consumer for 5 seconds. (handle=0x%x, seq_id=%d, cmd_id=%d, queue_size=%d)", handle, cmd->seq_id, cmd->id, out_queue.size());
|
||||
elapsed = 0;
|
||||
@@ -635,14 +636,12 @@ struct vdec_creation_lock
|
||||
|
||||
vdec_creation_lock()
|
||||
{
|
||||
locked.init();
|
||||
auto lk = locked.init();
|
||||
}
|
||||
};
|
||||
|
||||
extern bool try_lock_vdec_context_creation()
|
||||
{
|
||||
bool exist = false;
|
||||
|
||||
auto& lock = g_fxo->get<vdec_creation_lock>();
|
||||
auto reset = lock.locked.reset();
|
||||
|
||||
@@ -997,8 +996,10 @@ error_code cellVdecClose(ppu_thread& ppu, u32 handle)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVdecStartSeq(u32 handle)
|
||||
error_code cellVdecStartSeq(ppu_thread& ppu, u32 handle)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
cellVdec.warning("cellVdecStartSeq(handle=0x%x)", handle);
|
||||
|
||||
const auto vdec = idm::get<vdec_context>(handle);
|
||||
@@ -1047,8 +1048,10 @@ error_code cellVdecStartSeq(u32 handle)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVdecEndSeq(u32 handle)
|
||||
error_code cellVdecEndSeq(ppu_thread& ppu, u32 handle)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
cellVdec.warning("cellVdecEndSeq(handle=0x%x)", handle);
|
||||
|
||||
const auto vdec = idm::get<vdec_context>(handle);
|
||||
@@ -1078,8 +1081,10 @@ error_code cellVdecEndSeq(u32 handle)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVdecDecodeAu(u32 handle, CellVdecDecodeMode mode, vm::cptr<CellVdecAuInfo> auInfo)
|
||||
error_code cellVdecDecodeAu(ppu_thread& ppu, u32 handle, CellVdecDecodeMode mode, vm::cptr<CellVdecAuInfo> auInfo)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
cellVdec.trace("cellVdecDecodeAu(handle=0x%x, mode=%d, auInfo=*0x%x)", handle, +mode, auInfo);
|
||||
|
||||
const auto vdec = idm::get<vdec_context>(handle);
|
||||
@@ -1124,8 +1129,10 @@ error_code cellVdecDecodeAu(u32 handle, CellVdecDecodeMode mode, vm::cptr<CellVd
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVdecDecodeAuEx2(u32 handle, CellVdecDecodeMode mode, vm::cptr<CellVdecAuInfoEx2> auInfo)
|
||||
error_code cellVdecDecodeAuEx2(ppu_thread& ppu, u32 handle, CellVdecDecodeMode mode, vm::cptr<CellVdecAuInfoEx2> auInfo)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
cellVdec.todo("cellVdecDecodeAuEx2(handle=0x%x, mode=%d, auInfo=*0x%x)", handle, +mode, auInfo);
|
||||
|
||||
const auto vdec = idm::get<vdec_context>(handle);
|
||||
@@ -1178,8 +1185,10 @@ error_code cellVdecDecodeAuEx2(u32 handle, CellVdecDecodeMode mode, vm::cptr<Cel
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVdecGetPictureExt(u32 handle, vm::cptr<CellVdecPicFormat2> format, vm::ptr<u8> outBuff, u32 arg4)
|
||||
error_code cellVdecGetPictureExt(ppu_thread& ppu, u32 handle, vm::cptr<CellVdecPicFormat2> format, vm::ptr<u8> outBuff, u32 arg4)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
cellVdec.trace("cellVdecGetPictureExt(handle=0x%x, format=*0x%x, outBuff=*0x%x, arg4=*0x%x)", handle, format, outBuff, arg4);
|
||||
|
||||
const auto vdec = idm::get<vdec_context>(handle);
|
||||
@@ -1323,8 +1332,10 @@ error_code cellVdecGetPictureExt(u32 handle, vm::cptr<CellVdecPicFormat2> format
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellVdecGetPicture(u32 handle, vm::cptr<CellVdecPicFormat> format, vm::ptr<u8> outBuff)
|
||||
error_code cellVdecGetPicture(ppu_thread& ppu, u32 handle, vm::cptr<CellVdecPicFormat> format, vm::ptr<u8> outBuff)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
cellVdec.trace("cellVdecGetPicture(handle=0x%x, format=*0x%x, outBuff=*0x%x)", handle, format, outBuff);
|
||||
|
||||
if (!format)
|
||||
@@ -1339,11 +1350,13 @@ error_code cellVdecGetPicture(u32 handle, vm::cptr<CellVdecPicFormat> format, vm
|
||||
format2->unk0 = 0;
|
||||
format2->unk1 = 0;
|
||||
|
||||
return cellVdecGetPictureExt(handle, format2, outBuff, 0);
|
||||
return cellVdecGetPictureExt(ppu, handle, format2, outBuff, 0);
|
||||
}
|
||||
|
||||
error_code cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
|
||||
error_code cellVdecGetPicItem(ppu_thread& ppu, u32 handle, vm::pptr<CellVdecPicItem> picItem)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
cellVdec.trace("cellVdecGetPicItem(handle=0x%x, picItem=**0x%x)", handle, picItem);
|
||||
|
||||
const auto vdec = idm::get<vdec_context>(handle);
|
||||
|
||||
@@ -123,10 +123,10 @@ error_code cellVideoOutGetState(u32 videoOut, u32 deviceIndex, vm::ptr<CellVideo
|
||||
const auto& conf = g_fxo->get<rsx::avconf>();
|
||||
state->state = CELL_VIDEO_OUT_OUTPUT_STATE_ENABLED;
|
||||
state->colorSpace = CELL_VIDEO_OUT_COLOR_SPACE_RGB;
|
||||
state->displayMode.resolutionId = conf.state ? conf.resolution_id : g_video_out_resolution_id.at(g_cfg.video.resolution);
|
||||
state->displayMode.resolutionId = conf.state ? conf.resolution_id : ::at32(g_video_out_resolution_id, g_cfg.video.resolution);
|
||||
state->displayMode.scanMode = CELL_VIDEO_OUT_SCAN_MODE_PROGRESSIVE;
|
||||
state->displayMode.conversion = CELL_VIDEO_OUT_DISPLAY_CONVERSION_NONE;
|
||||
state->displayMode.aspect = conf.state ? conf.aspect : g_video_out_aspect_id.at(g_cfg.video.aspect_ratio);
|
||||
state->displayMode.aspect = conf.state ? conf.aspect : ::at32(g_video_out_aspect_id, g_cfg.video.aspect_ratio);
|
||||
state->displayMode.refreshRates = CELL_VIDEO_OUT_REFRESH_RATE_59_94HZ;
|
||||
|
||||
return CELL_OK;
|
||||
@@ -207,7 +207,7 @@ error_code cellVideoOutConfigure(u32 videoOut, vm::ptr<CellVideoOutConfiguration
|
||||
}
|
||||
|
||||
// Resolve 'auto' or unknown options to actual aspect ratio
|
||||
conf.aspect = g_video_out_aspect_id.at(g_cfg.video.aspect_ratio);
|
||||
conf.aspect = ::at32(g_video_out_aspect_id, g_cfg.video.aspect_ratio);
|
||||
}
|
||||
|
||||
cellSysutil.notice("Selected video configuration: resolutionId=0x%x, aspect=0x%x=>0x%x, format=0x%x", config->resolutionId, config->aspect, conf.aspect, config->format);
|
||||
@@ -242,9 +242,9 @@ error_code cellVideoOutGetConfiguration(u32 videoOut, vm::ptr<CellVideoOutConfig
|
||||
}
|
||||
else
|
||||
{
|
||||
config->resolutionId = g_video_out_resolution_id.at(g_cfg.video.resolution);
|
||||
config->resolutionId = ::at32(g_video_out_resolution_id, g_cfg.video.resolution);
|
||||
config->format = CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8R8G8B8;
|
||||
config->aspect = g_video_out_aspect_id.at(g_cfg.video.aspect_ratio);
|
||||
config->aspect = ::at32(g_video_out_aspect_id, g_cfg.video.aspect_ratio);
|
||||
|
||||
CellVideoOutResolution res;
|
||||
ensure(_IntGetResolutionInfo(config->resolutionId, &res) == CELL_OK); // "Invalid video configuration"
|
||||
@@ -295,10 +295,10 @@ error_code cellVideoOutGetDeviceInfo(u32 videoOut, u32 deviceIndex, vm::ptr<Cell
|
||||
info->colorInfo.whiteX = 0xFFFF;
|
||||
info->colorInfo.whiteY = 0xFFFF;
|
||||
info->colorInfo.gamma = 100;
|
||||
info->availableModes[0].aspect = g_video_out_aspect_id.at(g_cfg.video.aspect_ratio);
|
||||
info->availableModes[0].aspect = ::at32(g_video_out_aspect_id, g_cfg.video.aspect_ratio);
|
||||
info->availableModes[0].conversion = CELL_VIDEO_OUT_DISPLAY_CONVERSION_NONE;
|
||||
info->availableModes[0].refreshRates = CELL_VIDEO_OUT_REFRESH_RATE_60HZ | CELL_VIDEO_OUT_REFRESH_RATE_59_94HZ;
|
||||
info->availableModes[0].resolutionId = g_video_out_resolution_id.at(g_cfg.video.resolution);
|
||||
info->availableModes[0].resolutionId = ::at32(g_video_out_resolution_id, g_cfg.video.resolution);
|
||||
info->availableModes[0].scanMode = CELL_VIDEO_OUT_SCAN_MODE_PROGRESSIVE;
|
||||
|
||||
if (g_cfg.video.enable_3d && g_cfg.video.resolution == video_resolution::_720)
|
||||
@@ -335,12 +335,12 @@ error_code cellVideoOutGetResolutionAvailability(u32 videoOut, u32 resolutionId,
|
||||
case CELL_VIDEO_OUT_PRIMARY:
|
||||
{
|
||||
// NOTE: Result is boolean
|
||||
if (aspect != CELL_VIDEO_OUT_ASPECT_AUTO && aspect != static_cast<u32>(g_video_out_aspect_id.at(g_cfg.video.aspect_ratio)))
|
||||
if (aspect != CELL_VIDEO_OUT_ASPECT_AUTO && aspect != static_cast<u32>(::at32(g_video_out_aspect_id, g_cfg.video.aspect_ratio)))
|
||||
{
|
||||
return not_an_error(0);
|
||||
}
|
||||
|
||||
if (resolutionId == static_cast<u32>(g_video_out_resolution_id.at(g_cfg.video.resolution)))
|
||||
if (resolutionId == static_cast<u32>(::at32(g_video_out_resolution_id, g_cfg.video.resolution)))
|
||||
{
|
||||
// Perfect match
|
||||
return not_an_error(1);
|
||||
|
||||
+641
-348
File diff suppressed because it is too large
Load Diff
@@ -1157,9 +1157,9 @@ struct SceNpScoreRankData
|
||||
be_t<u32> serialRank;
|
||||
be_t<u32> rank;
|
||||
be_t<u32> highestRank;
|
||||
be_t<s64> scoreValue;
|
||||
be_t<s32> hasGameData;
|
||||
u8 pad0[4];
|
||||
be_t<s64> scoreValue;
|
||||
CellRtcTick recordDate;
|
||||
};
|
||||
|
||||
@@ -1393,14 +1393,14 @@ struct SceNpMatchingReqRange
|
||||
|
||||
struct SceNpScoreVariableSizeGameInfo
|
||||
{
|
||||
be_t<u64> infoSize;
|
||||
be_t<u32> infoSize;
|
||||
u8 data[SCE_NP_SCORE_VARIABLE_SIZE_GAMEINFO_MAXSIZE];
|
||||
u8 pad[3];
|
||||
};
|
||||
|
||||
struct SceNpScoreRecordOptParam
|
||||
{
|
||||
be_t<u64> size;
|
||||
be_t<u32> size;
|
||||
vm::bptr<SceNpScoreVariableSizeGameInfo> vsInfo;
|
||||
vm::bptr<CellRtcTick> reserved;
|
||||
};
|
||||
|
||||
@@ -237,7 +237,7 @@ error_code sceNpMatching2Init(u32 stackSize, s32 priority)
|
||||
|
||||
error_code sceNpMatching2Init2(u64 stackSize, s32 priority, vm::ptr<SceNpMatching2UtilityInitParam> param)
|
||||
{
|
||||
sceNp2.todo("sceNpMatching2Init2(stackSize=0x%x, priority=%d, param=*0x%x)", stackSize, priority, param);
|
||||
sceNp2.warning("sceNpMatching2Init2(stackSize=0x%x, priority=%d, param=*0x%x)", stackSize, priority, param);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
@@ -319,7 +319,7 @@ error_code sceNpMatching2Term2()
|
||||
|
||||
error_code sceNpMatching2DestroyContext(SceNpMatching2ContextId ctxId)
|
||||
{
|
||||
sceNp2.todo("sceNpMatching2DestroyContext(ctxId=%d)", ctxId);
|
||||
sceNp2.warning("sceNpMatching2DestroyContext(ctxId=%d)", ctxId);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
@@ -611,11 +611,20 @@ error_code sceNpMatching2SignalingGetConnectionInfo(
|
||||
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!connInfo)
|
||||
{
|
||||
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
|
||||
const auto si = sigh.get_sig2_infos(roomId, memberId);
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case SCE_NP_SIGNALING_CONN_INFO_RTT:
|
||||
{
|
||||
connInfo->rtt = 20000; // HACK
|
||||
connInfo->rtt = si.rtt;
|
||||
sceNp2.warning("Returning a RTT of %d microseconds", connInfo->rtt);
|
||||
break;
|
||||
}
|
||||
case SCE_NP_SIGNALING_CONN_INFO_BANDWIDTH:
|
||||
@@ -625,22 +634,17 @@ error_code sceNpMatching2SignalingGetConnectionInfo(
|
||||
}
|
||||
case SCE_NP_SIGNALING_CONN_INFO_PEER_NPID:
|
||||
{
|
||||
// TODO: need an update to whole signaling as matching2 signaling ignores npids atm
|
||||
sceNp2.fatal("sceNpMatching2SignalingGetConnectionInfo Unimplemented SCE_NP_SIGNALING_CONN_INFO_PEER_NPID");
|
||||
connInfo->npId = si.npid;
|
||||
break;
|
||||
}
|
||||
case SCE_NP_SIGNALING_CONN_INFO_PEER_ADDRESS:
|
||||
{
|
||||
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
|
||||
const auto si = sigh.get_sig2_infos(roomId, memberId);
|
||||
connInfo->address.port = std::bit_cast<u16, be_t<u16>>(si.port);
|
||||
connInfo->address.addr.np_s_addr = si.addr;
|
||||
break;
|
||||
}
|
||||
case SCE_NP_SIGNALING_CONN_INFO_MAPPED_ADDRESS:
|
||||
{
|
||||
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
|
||||
const auto si = sigh.get_sig2_infos(roomId, memberId);
|
||||
connInfo->address.port = std::bit_cast<u16, be_t<u16>>(si.mapped_port);
|
||||
connInfo->address.addr.np_s_addr = si.mapped_addr;
|
||||
break;
|
||||
@@ -652,8 +656,7 @@ error_code sceNpMatching2SignalingGetConnectionInfo(
|
||||
}
|
||||
default:
|
||||
{
|
||||
sceNp2.fatal("sceNpMatching2SignalingGetConnectionInfo Unimplemented code: %d", code);
|
||||
return CELL_OK;
|
||||
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -715,6 +718,16 @@ error_code sceNpMatching2AbortRequest(SceNpMatching2ContextId ctxId, SceNpMatchi
|
||||
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
if (!ctxId)
|
||||
{
|
||||
return SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
|
||||
}
|
||||
|
||||
if (!check_match2_context(ctxId))
|
||||
{
|
||||
return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1349,7 +1362,7 @@ error_code sceNpMatching2SetDefaultRequestOptParam(SceNpMatching2ContextId ctxId
|
||||
|
||||
error_code sceNpMatching2RegisterRoomEventCallback(SceNpMatching2ContextId ctxId, vm::ptr<SceNpMatching2RoomEventCallback> cbFunc, vm::ptr<void> cbFuncArg)
|
||||
{
|
||||
sceNp2.todo("sceNpMatching2RegisterRoomEventCallback(ctxId=%d, cbFunc=*0x%x, cbFuncArg=*0x%x)", ctxId, cbFunc, cbFuncArg);
|
||||
sceNp2.warning("sceNpMatching2RegisterRoomEventCallback(ctxId=%d, cbFunc=*0x%x, cbFuncArg=*0x%x)", ctxId, cbFunc, cbFuncArg);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
@@ -1546,7 +1559,7 @@ error_code sceNpMatching2SetLobbyMemberDataInternal(
|
||||
|
||||
error_code sceNpMatching2RegisterRoomMessageCallback(SceNpMatching2ContextId ctxId, vm::ptr<SceNpMatching2RoomMessageCallback> cbFunc, vm::ptr<void> cbFuncArg)
|
||||
{
|
||||
sceNp2.todo("sceNpMatching2RegisterRoomMessageCallback(ctxId=%d, cbFunc=*0x%x, cbFuncArg=*0x%x)", ctxId, cbFunc, cbFuncArg);
|
||||
sceNp2.warning("sceNpMatching2RegisterRoomMessageCallback(ctxId=%d, cbFunc=*0x%x, cbFuncArg=*0x%x)", ctxId, cbFunc, cbFuncArg);
|
||||
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
|
||||
|
||||
@@ -458,7 +458,7 @@ error_code sceNpTrophyCreateContext(vm::ptr<u32> context, vm::cptr<SceNpCommunic
|
||||
name_sv = name_sv.substr(0, pos);
|
||||
}
|
||||
|
||||
sceNpTrophy.warning("sceNpTrophyCreateContext(): data='%s' term='%c' (0x%x) num=%d", name_sv, commId->data[9], commId->data[9], commId->num);
|
||||
sceNpTrophy.warning("sceNpTrophyCreateContext(): data='%s' term=0x%x (0x%x) num=%d", name_sv, commId->data[9], commId->data[9], commId->num);
|
||||
|
||||
// append the commId number as "_xx"
|
||||
std::string name = fmt::format("%s_%02d", name_sv, commId->num);
|
||||
@@ -604,7 +604,7 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,
|
||||
|
||||
if (!stream && Emu.GetCat() == "GD")
|
||||
{
|
||||
sceNpTrophy.warning("sceNpTrophyRegisterContext failed to open trophy file from boot path: '%s'", trp_path);
|
||||
sceNpTrophy.warning("sceNpTrophyRegisterContext failed to open trophy file from boot path: '%s' (%s)", trp_path, fs::g_tls_error);
|
||||
trp_path = vfs::get("/dev_bdvd/PS3_GAME/TROPDIR/" + ctxt->trp_name + "/TROPHY.TRP");
|
||||
stream.open(trp_path);
|
||||
}
|
||||
@@ -612,7 +612,8 @@ error_code sceNpTrophyRegisterContext(ppu_thread& ppu, u32 context, u32 handle,
|
||||
// check if exists and opened
|
||||
if (!stream)
|
||||
{
|
||||
return {SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST, trp_path};
|
||||
const std::string msg = fmt::format("Failed to open trophy file: '%s' (%s)", trp_path, fs::g_tls_error);
|
||||
return {SCE_NP_TROPHY_ERROR_CONF_DOES_NOT_EXIST, msg};
|
||||
}
|
||||
|
||||
TRPLoader trp(stream);
|
||||
|
||||
@@ -58,7 +58,7 @@ sce_np_tus_title_context* sce_np_tus_manager::get_title_context(s32 titleCtxId)
|
||||
{
|
||||
if (title_contexts.find(titleCtxId) != title_contexts.end())
|
||||
{
|
||||
return &title_contexts.at(titleCtxId);
|
||||
return &::at32(title_contexts, titleCtxId);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@@ -81,7 +81,7 @@ s32 sce_np_tus_manager::add_transaction_context(s32 titleCtxId)
|
||||
sce_np_tus_transaction_context new_transaction;
|
||||
new_transaction.id = next_transaction_context_id;
|
||||
|
||||
if (title_contexts.at(titleCtxId).transaction_contexts.emplace(next_transaction_context_id, new_transaction).second)
|
||||
if (::at32(title_contexts, titleCtxId).transaction_contexts.emplace(next_transaction_context_id, new_transaction).second)
|
||||
{
|
||||
return next_transaction_context_id++;
|
||||
}
|
||||
@@ -122,7 +122,7 @@ sce_np_tus_transaction_context* sce_np_tus_manager::get_transaction_context(s32
|
||||
|
||||
if (transactions.find(transId) != transactions.end())
|
||||
{
|
||||
return &transactions.at(transId);
|
||||
return &::at32(transactions, transId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1224,7 +1224,7 @@ error_code sceNpTusDeleteMultiSlotVariableVUserAsync(s32 transId, vm::cptr<SceNp
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpTusSetData(s32 transId, vm::cptr<SceNpId> targetNpId, SceNpTusSlotId slotId, u64 totalSize, u64 sendSize, vm::cptr<void> data, vm::cptr<SceNpTusDataInfo> info, u64 infoStructSize, vm::ptr<SceNpTusSetDataOptParam> option)
|
||||
error_code sceNpTusSetData(s32 transId, vm::cptr<SceNpId> targetNpId, SceNpTusSlotId slotId, u32 totalSize, u32 sendSize, vm::cptr<void> data, vm::cptr<SceNpTusDataInfo> info, u64 infoStructSize, vm::ptr<SceNpTusSetDataOptParam> option)
|
||||
{
|
||||
sceNpTus.todo("sceNpTusSetData(transId=%d, targetNpId=*0x%x, slotId=%d, totalSize=%d, sendSize=%d, data=*0x%x, info=*0x%x, infoStructSize=%d, option=*0x%x)", transId, targetNpId, slotId, totalSize, sendSize, data, info, infoStructSize, option);
|
||||
|
||||
@@ -1255,7 +1255,7 @@ error_code sceNpTusSetData(s32 transId, vm::cptr<SceNpId> targetNpId, SceNpTusSl
|
||||
return not_an_error(data_size);
|
||||
}
|
||||
|
||||
error_code sceNpTusSetDataVUser(s32 transId, vm::cptr<SceNpTusVirtualUserId> targetVirtualUserId, SceNpTusSlotId slotId, u64 totalSize, u64 sendSize, vm::cptr<void> data, vm::cptr<SceNpTusDataInfo> info, u64 infoStructSize, vm::ptr<SceNpTusSetDataOptParam> option)
|
||||
error_code sceNpTusSetDataVUser(s32 transId, vm::cptr<SceNpTusVirtualUserId> targetVirtualUserId, SceNpTusSlotId slotId, u32 totalSize, u32 sendSize, vm::cptr<void> data, vm::cptr<SceNpTusDataInfo> info, u64 infoStructSize, vm::ptr<SceNpTusSetDataOptParam> option)
|
||||
{
|
||||
sceNpTus.todo("sceNpTusSetDataAsync(transId=%d, targetVirtualUserId=*0x%x, slotId=%d, totalSize=%d, sendSize=%d, data=*0x%x, info=*0x%x, infoStructSize=%d, option=*0x%x)", transId, targetVirtualUserId, slotId, totalSize, sendSize, data, info, infoStructSize, option);
|
||||
|
||||
@@ -1286,7 +1286,7 @@ error_code sceNpTusSetDataVUser(s32 transId, vm::cptr<SceNpTusVirtualUserId> tar
|
||||
return not_an_error(data_size);
|
||||
}
|
||||
|
||||
error_code sceNpTusSetDataAsync(s32 transId, vm::cptr<SceNpId> targetNpId, SceNpTusSlotId slotId, u64 totalSize, u64 sendSize, vm::cptr<void> data, vm::cptr<SceNpTusDataInfo> info, u64 infoStructSize, vm::ptr<SceNpTusSetDataOptParam> option)
|
||||
error_code sceNpTusSetDataAsync(s32 transId, vm::cptr<SceNpId> targetNpId, SceNpTusSlotId slotId, u32 totalSize, u32 sendSize, vm::cptr<void> data, vm::cptr<SceNpTusDataInfo> info, u64 infoStructSize, vm::ptr<SceNpTusSetDataOptParam> option)
|
||||
{
|
||||
sceNpTus.todo("sceNpTusSetDataAsync(transId=%d, targetNpId=*0x%x, slotId=%d, totalSize=%d, sendSize=%d, data=*0x%x, info=*0x%x, infoStructSize=%d, option=*0x%x)", transId, targetNpId, slotId, totalSize, sendSize, data, info, infoStructSize, option);
|
||||
|
||||
@@ -1316,7 +1316,7 @@ error_code sceNpTusSetDataAsync(s32 transId, vm::cptr<SceNpId> targetNpId, SceNp
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpTusSetDataVUserAsync(s32 transId, vm::cptr<SceNpTusVirtualUserId> targetVirtualUserId, SceNpTusSlotId slotId, u64 totalSize, u64 sendSize, vm::cptr<void> data, vm::cptr<SceNpTusDataInfo> info, u64 infoStructSize, vm::ptr<SceNpTusSetDataOptParam> option)
|
||||
error_code sceNpTusSetDataVUserAsync(s32 transId, vm::cptr<SceNpTusVirtualUserId> targetVirtualUserId, SceNpTusSlotId slotId, u32 totalSize, u32 sendSize, vm::cptr<void> data, vm::cptr<SceNpTusDataInfo> info, u64 infoStructSize, vm::ptr<SceNpTusSetDataOptParam> option)
|
||||
{
|
||||
sceNpTus.todo("sceNpTusSetDataAsync(transId=%d, targetVirtualUserId=*0x%x, slotId=%d, totalSize=%d, sendSize=%d, data=*0x%x, info=*0x%x, infoStructSize=%d, option=*0x%x)", transId, targetVirtualUserId, slotId, totalSize, sendSize, data, info, infoStructSize, option);
|
||||
|
||||
@@ -1346,7 +1346,7 @@ error_code sceNpTusSetDataVUserAsync(s32 transId, vm::cptr<SceNpTusVirtualUserId
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpTusGetData(s32 transId, vm::cptr<SceNpId> targetNpId, SceNpTusSlotId slotId, vm::ptr<SceNpTusDataStatus> dataStatus, u64 dataStatusSize, vm::ptr<void> data, u64 recvSize, vm::ptr<void> option)
|
||||
error_code sceNpTusGetData(s32 transId, vm::cptr<SceNpId> targetNpId, SceNpTusSlotId slotId, vm::ptr<SceNpTusDataStatus> dataStatus, u32 dataStatusSize, vm::ptr<void> data, u32 recvSize, vm::ptr<void> option)
|
||||
{
|
||||
sceNpTus.todo("sceNpTusGetData(transId=%d, targetNpId=*0x%x, slotId=%d, dataStatus=*0x%x, dataStatusSize=%d, data=*0x%x, recvSize=%d, option=*0x%x)", transId, targetNpId, slotId, dataStatus, dataStatusSize, data, recvSize, option);
|
||||
|
||||
@@ -1377,7 +1377,7 @@ error_code sceNpTusGetData(s32 transId, vm::cptr<SceNpId> targetNpId, SceNpTusSl
|
||||
return not_an_error(data_size);
|
||||
}
|
||||
|
||||
error_code sceNpTusGetDataVUser(s32 transId, vm::cptr<SceNpTusVirtualUserId> targetVirtualUserId, SceNpTusSlotId slotId, vm::ptr<SceNpTusDataStatus> dataStatus, u64 dataStatusSize, vm::ptr<void> data, u64 recvSize, vm::ptr<void> option)
|
||||
error_code sceNpTusGetDataVUser(s32 transId, vm::cptr<SceNpTusVirtualUserId> targetVirtualUserId, SceNpTusSlotId slotId, vm::ptr<SceNpTusDataStatus> dataStatus, u32 dataStatusSize, vm::ptr<void> data, u32 recvSize, vm::ptr<void> option)
|
||||
{
|
||||
sceNpTus.todo("sceNpTusGetDataVUser(transId=%d, targetVirtualUserId=*0x%x, slotId=%d, dataStatus=*0x%x, dataStatusSize=%d, data=*0x%x, recvSize=%d, option=*0x%x)", transId, targetVirtualUserId, slotId, dataStatus, dataStatusSize, data, recvSize, option);
|
||||
|
||||
@@ -1408,7 +1408,7 @@ error_code sceNpTusGetDataVUser(s32 transId, vm::cptr<SceNpTusVirtualUserId> tar
|
||||
return not_an_error(data_size);
|
||||
}
|
||||
|
||||
error_code sceNpTusGetDataAsync(s32 transId, vm::cptr<SceNpId> targetNpId, SceNpTusSlotId slotId, vm::ptr<SceNpTusDataStatus> dataStatus, u64 dataStatusSize, vm::ptr<void> data, u64 recvSize, vm::ptr<void> option)
|
||||
error_code sceNpTusGetDataAsync(s32 transId, vm::cptr<SceNpId> targetNpId, SceNpTusSlotId slotId, vm::ptr<SceNpTusDataStatus> dataStatus, u32 dataStatusSize, vm::ptr<void> data, u32 recvSize, vm::ptr<void> option)
|
||||
{
|
||||
sceNpTus.todo("sceNpTusGetDataAsync(transId=%d, targetNpId=*0x%x, slotId=%d, dataStatus=*0x%x, dataStatusSize=%d, data=*0x%x, recvSize=%d, option=*0x%x)", transId, targetNpId, slotId, dataStatus, dataStatusSize, data, recvSize, option);
|
||||
|
||||
@@ -1443,7 +1443,7 @@ error_code sceNpTusGetDataAsync(s32 transId, vm::cptr<SceNpId> targetNpId, SceNp
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpTusGetDataVUserAsync(s32 transId, vm::cptr<SceNpTusVirtualUserId> targetVirtualUserId, SceNpTusSlotId slotId, vm::ptr<SceNpTusDataStatus> dataStatus, u64 dataStatusSize, vm::ptr<void> data, u64 recvSize, vm::ptr<void> option)
|
||||
error_code sceNpTusGetDataVUserAsync(s32 transId, vm::cptr<SceNpTusVirtualUserId> targetVirtualUserId, SceNpTusSlotId slotId, vm::ptr<SceNpTusDataStatus> dataStatus, u32 dataStatusSize, vm::ptr<void> data, u32 recvSize, vm::ptr<void> option)
|
||||
{
|
||||
sceNpTus.todo("sceNpTusGetDataVUser(transId=%d, targetVirtualUserId=*0x%x, slotId=%d, dataStatus=*0x%x, dataStatusSize=%d, data=*0x%x, recvSize=%d, option=*0x%x)", transId, targetVirtualUserId, slotId, dataStatus, dataStatusSize, data, recvSize, option);
|
||||
|
||||
@@ -1968,7 +1968,7 @@ error_code sceNpTusDeleteMultiSlotDataVUserAsync(s32 transId, vm::cptr<SceNpTusV
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpTssGetData(s32 transId, SceNpTssSlotId slotId, vm::ptr<SceNpTssDataStatus> dataStatus, u64 dataStatusSize, vm::ptr<void> data, u64 recvSize, vm::ptr<SceNpTssGetDataOptParam> option)
|
||||
error_code sceNpTssGetData(s32 transId, SceNpTssSlotId slotId, vm::ptr<SceNpTssDataStatus> dataStatus, u32 dataStatusSize, vm::ptr<void> data, u32 recvSize, vm::ptr<SceNpTssGetDataOptParam> option)
|
||||
{
|
||||
sceNpTus.todo("sceNpTssGetData(transId=%d, slotId=%d, dataStatus=*0x%x, dataStatusSize=%d, data=*0x%x, recvSize=%d, option=*0x%x)", transId, slotId, dataStatus, dataStatusSize, data, recvSize, option);
|
||||
|
||||
@@ -1993,7 +1993,7 @@ error_code sceNpTssGetData(s32 transId, SceNpTssSlotId slotId, vm::ptr<SceNpTssD
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sceNpTssGetDataAsync(s32 transId, SceNpTssSlotId slotId, vm::ptr<SceNpTssDataStatus> dataStatus, u64 dataStatusSize, vm::ptr<void> data, u64 recvSize, vm::ptr<SceNpTssGetDataOptParam> option)
|
||||
error_code sceNpTssGetDataAsync(s32 transId, SceNpTssSlotId slotId, vm::ptr<SceNpTssDataStatus> dataStatus, u32 dataStatusSize, vm::ptr<void> data, u32 recvSize, vm::ptr<SceNpTssGetDataOptParam> option)
|
||||
{
|
||||
sceNpTus.todo("sceNpTssGetDataAsync(transId=%d, slotId=%d, dataStatus=*0x%x, dataStatusSize=%d, data=*0x%x, recvSize=%d, option=*0x%x)", transId, slotId, dataStatus, dataStatusSize, data, recvSize, option);
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ struct sce_np_util_manager
|
||||
}
|
||||
};
|
||||
|
||||
error_code sceNpUtilBandwidthTestInitStart(ppu_thread& ppu, u32 prio, u32 stack)
|
||||
error_code sceNpUtilBandwidthTestInitStart([[maybe_unused]] ppu_thread& ppu, u32 prio, u32 stack)
|
||||
{
|
||||
sceNpUtil.todo("sceNpUtilBandwidthTestInitStart(prio=%d, stack=%d)", prio, stack);
|
||||
|
||||
@@ -101,7 +101,7 @@ error_code sceNpUtilBandwidthTestGetStatus()
|
||||
return not_an_error(util_manager.bandwidth_test_thread->status);
|
||||
}
|
||||
|
||||
error_code sceNpUtilBandwidthTestShutdown(ppu_thread& ppu, vm::ptr<SceNpUtilBandwidthTestResult> result)
|
||||
error_code sceNpUtilBandwidthTestShutdown([[maybe_unused]] ppu_thread& ppu, vm::ptr<SceNpUtilBandwidthTestResult> result)
|
||||
{
|
||||
sceNpUtil.warning("sceNpUtilBandwidthTestShutdown(result=*0x%x)", result);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#if !defined(_MSC_VER)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#pragma GCC diagnostic ignored "-Wuninitialized"
|
||||
#endif
|
||||
|
||||
extern bool is_debugger_present();
|
||||
@@ -5311,7 +5312,7 @@ auto STVLX()
|
||||
{
|
||||
const u64 addr = op.ra ? a + b : b;
|
||||
const u32 tail = u32(addr & 15);
|
||||
u8* ptr = vm::_ptr<u8>(addr & -16);
|
||||
u8* ptr = vm::_ptr<u8>(addr);
|
||||
for (u32 j = 0; j < 16 - tail; j++)
|
||||
ptr[j] = s.u8r[j];
|
||||
};
|
||||
|
||||
+103
-12
@@ -38,6 +38,8 @@ extern void sys_initialize_tls(ppu_thread&, u64, u32, u32, u32);
|
||||
// HLE function name cache
|
||||
std::vector<std::string> g_ppu_function_names;
|
||||
|
||||
atomic_t<u32> liblv2_begin = 0, liblv2_end = 0;
|
||||
|
||||
extern u32 ppu_generate_id(std::string_view name)
|
||||
{
|
||||
// Symbol name suffix
|
||||
@@ -83,7 +85,7 @@ void ppu_module_manager::register_module(ppu_static_module* _module)
|
||||
|
||||
ppu_static_function& ppu_module_manager::access_static_function(const char* _module, u32 fnid)
|
||||
{
|
||||
auto& res = ppu_module_manager::s_module_map.at(_module)->functions[fnid];
|
||||
auto& res = ::at32(ppu_module_manager::s_module_map, _module)->functions[fnid];
|
||||
|
||||
if (res.name)
|
||||
{
|
||||
@@ -95,7 +97,7 @@ ppu_static_function& ppu_module_manager::access_static_function(const char* _mod
|
||||
|
||||
ppu_static_variable& ppu_module_manager::access_static_variable(const char* _module, u32 vnid)
|
||||
{
|
||||
auto& res = ppu_module_manager::s_module_map.at(_module)->variables[vnid];
|
||||
auto& res = ::at32(ppu_module_manager::s_module_map, _module)->variables[vnid];
|
||||
|
||||
if (res.name)
|
||||
{
|
||||
@@ -338,20 +340,20 @@ static void ppu_initialize_modules(ppu_linkage_info* link, utils::serial* ar = n
|
||||
while (true)
|
||||
{
|
||||
const std::string name = ar.operator std::string();
|
||||
|
||||
|
||||
if (name.empty())
|
||||
{
|
||||
// Null termination
|
||||
break;
|
||||
}
|
||||
|
||||
const auto _module = manager.at(name);
|
||||
const auto _module = ::at32(manager, name);
|
||||
|
||||
auto& variable = _module->variables;
|
||||
|
||||
|
||||
for (u32 i = 0, end = ar.operator usz(); i < end; i++)
|
||||
{
|
||||
auto* ptr = &variable.at(ar.operator u32());
|
||||
auto* ptr = &::at32(variable, ar.operator u32());
|
||||
ptr->addr = ar.operator u32();
|
||||
ensure(!!ptr->var);
|
||||
}
|
||||
@@ -680,7 +682,7 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo
|
||||
{
|
||||
const u32 fnid = fnids[i];
|
||||
const u32 faddr = faddrs[i];
|
||||
ppu_loader.notice("**** %s export: [%s] (0x%08x) at 0x%x", module_name, ppu_get_function_name(module_name, fnid), fnid, faddr);
|
||||
ppu_loader.notice("**** %s export: [%s] (0x%08x) at 0x%x [at:0x%x]", module_name, ppu_get_function_name(module_name, fnid), fnid, faddr, vm::read32(faddr));
|
||||
|
||||
// Function linkage info
|
||||
auto& flink = mlink.functions[fnid];
|
||||
@@ -697,7 +699,7 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo
|
||||
//else
|
||||
{
|
||||
// Static function
|
||||
const auto _sf = _sm && _sm->functions.count(fnid) ? &_sm->functions.at(fnid) : nullptr;
|
||||
const auto _sf = _sm && _sm->functions.count(fnid) ? &::at32(_sm->functions, fnid) : nullptr;
|
||||
|
||||
if (_sf && (_sf->flags & MFF_FORCED_HLE))
|
||||
{
|
||||
@@ -1069,6 +1071,77 @@ void try_spawn_ppu_if_exclusive_program(const ppu_module& m)
|
||||
}
|
||||
}
|
||||
|
||||
struct prx_names_table
|
||||
{
|
||||
shared_mutex mutex;
|
||||
std::set<std::string, std::less<>> registered;
|
||||
atomic_t<const char*> lut[0x1000'0000 / 0x1'0000]{};
|
||||
|
||||
SAVESTATE_INIT_POS(4.1); // Dependency on lv2_obj
|
||||
|
||||
prx_names_table() noexcept
|
||||
{
|
||||
idm::select<lv2_obj, lv2_prx>([this](u32, lv2_prx& prx)
|
||||
{
|
||||
install(prx.name, prx);
|
||||
});
|
||||
}
|
||||
|
||||
void install(std::string_view name, lv2_prx& prx)
|
||||
{
|
||||
if (name.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (name.ends_with(".sprx"sv) && name.size() > (".sprx"sv).size())
|
||||
{
|
||||
name = name.substr(0, name.size() - (".sprx"sv).size());
|
||||
}
|
||||
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
const auto ptr = registered.emplace(name).first->c_str();
|
||||
|
||||
for (auto& seg : prx.segs)
|
||||
{
|
||||
if (!seg.size)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Doesn't support addresses above 256MB because it wastes memory and is very unlikely (if somehow does occur increase it)
|
||||
const u32 max0 = (seg.addr + seg.size - 1) >> 16;
|
||||
const u32 max = std::min<u32>(std::size(lut), max0);
|
||||
|
||||
if (max0 > max)
|
||||
{
|
||||
ppu_loader.error("Skipping PRX name registeration: %s, max=0x%x", name, max0 << 16);
|
||||
}
|
||||
|
||||
for (u32 i = seg.addr >> 16; i <= max; i++)
|
||||
{
|
||||
lut[i].release(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const char* get_prx_name_by_cia(u32 addr)
|
||||
{
|
||||
if (auto t = g_fxo->try_get<prx_names_table>())
|
||||
{
|
||||
addr >>= 16;
|
||||
|
||||
if (addr < std::size(t->lut))
|
||||
{
|
||||
return t->lut[addr];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& path, s64 file_offset, utils::serial* ar)
|
||||
{
|
||||
if (elf != elf_error::ok)
|
||||
@@ -1215,12 +1288,12 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
|
||||
{
|
||||
const auto& rel = reinterpret_cast<const ppu_prx_relocation_info&>(prog.bin[i]);
|
||||
|
||||
if (rel.offset >= prx->segs.at(rel.index_addr).size)
|
||||
if (rel.offset >= ::at32(prx->segs, rel.index_addr).size)
|
||||
{
|
||||
fmt::throw_exception("Relocation offset out of segment memory! (offset=0x%x, index_addr=%u)", rel.offset, rel.index_addr);
|
||||
}
|
||||
|
||||
const u32 data_base = rel.index_value == 0xFF ? 0 : prx->segs.at(rel.index_value).addr;
|
||||
const u32 data_base = rel.index_value == 0xFF ? 0 : ::at32(prx->segs, rel.index_value).addr;
|
||||
|
||||
if (rel.index_value != 0xFF && !data_base)
|
||||
{
|
||||
@@ -1228,7 +1301,7 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
|
||||
}
|
||||
|
||||
ppu_reloc _rel;
|
||||
const u32 raddr = _rel.addr = vm::cast(prx->segs.at(rel.index_addr).addr + rel.offset);
|
||||
const u32 raddr = _rel.addr = vm::cast(::at32(prx->segs, rel.index_addr).addr + rel.offset);
|
||||
const u32 rtype = _rel.type = rel.type;
|
||||
const u64 rdata = _rel.data = data_base + rel.ptr.addr();
|
||||
prx->relocs.emplace_back(_rel);
|
||||
@@ -1362,11 +1435,20 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
|
||||
prx->path = path;
|
||||
prx->offset = file_offset;
|
||||
|
||||
g_fxo->need<prx_names_table>();
|
||||
g_fxo->get<prx_names_table>().install(prx->name, *prx);
|
||||
|
||||
sha1_finish(&sha, prx->sha1);
|
||||
|
||||
// Format patch name
|
||||
std::string hash = fmt::format("PRX-%s", fmt::base57(prx->sha1));
|
||||
|
||||
if (prx->path.ends_with("sys/external/liblv2.sprx"sv))
|
||||
{
|
||||
liblv2_begin = prx->segs[0].addr;
|
||||
liblv2_end = prx->segs[0].addr + prx->segs[0].size;
|
||||
}
|
||||
|
||||
std::basic_string<u32> applied;
|
||||
|
||||
for (usz i = 0; i < prx->segs.size(); i++)
|
||||
@@ -1441,6 +1523,12 @@ void ppu_unload_prx(const lv2_prx& prx)
|
||||
// }
|
||||
//}
|
||||
|
||||
if (prx.path.ends_with("sys/external/liblv2.sprx"sv))
|
||||
{
|
||||
liblv2_begin = 0;
|
||||
liblv2_end = 0;
|
||||
}
|
||||
|
||||
// Format patch name
|
||||
std::string hash = fmt::format("PRX-%s", fmt::base57(prx.sha1));
|
||||
|
||||
@@ -1935,6 +2023,9 @@ bool ppu_load_exec(const ppu_exec_object& elf, utils::serial* ar)
|
||||
void init_fxo_for_exec(utils::serial* ar, bool full);
|
||||
init_fxo_for_exec(ar, false);
|
||||
|
||||
liblv2_begin = 0;
|
||||
liblv2_end = 0;
|
||||
|
||||
if (!load_libs.empty())
|
||||
{
|
||||
for (const auto& name : load_libs)
|
||||
@@ -2449,7 +2540,7 @@ bool ppu_load_rel_exec(const ppu_rel_object& elf)
|
||||
const auto& s = *ptr;
|
||||
|
||||
ppu_loader.notice("** Section: sh_type=0x%x, addr=0x%llx, size=0x%llx, flags=0x%x", std::bit_cast<u32>(s.sh_type), s.sh_addr, s.sh_size, s._sh_flags);
|
||||
|
||||
|
||||
if (s.sh_type == sec_type::sht_progbits && s.sh_size && s.sh_flags().all_of(sh_flag::shf_alloc))
|
||||
{
|
||||
ppu_segment _sec;
|
||||
|
||||
@@ -294,7 +294,7 @@ inline RT ppu_execute(ppu_thread& ppu, Args... args)
|
||||
return func(ppu, args...);
|
||||
}
|
||||
|
||||
#define BIND_FUNC_WITH_BLR(func) BIND_FUNC(func, ppu.cia = static_cast<u32>(ppu.lr) & ~3)
|
||||
#define BIND_FUNC_WITH_BLR(func) BIND_FUNC(func, if (cpu_flag::again - ppu.state) ppu.cia = static_cast<u32>(ppu.lr) & ~3)
|
||||
|
||||
#define REG_FNID(_module, nid, func) ppu_module_manager::register_static_function<&func>(#_module, ppu_select_name(#func, nid), BIND_FUNC_WITH_BLR(func), ppu_generate_id(nid))
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ class ppu_decoder
|
||||
for (u32 j = 0; j < 1u << sh; j++)
|
||||
{
|
||||
const u32 k = (((i << (count - v.magn)) | v.value) << sh) | j;
|
||||
m_table.at((k << 6) | main_op) = k & 1 ? v.ptr_rc : v.ptr0;
|
||||
::at32(m_table, (k << 6) | main_op) = k & 1 ? v.ptr_rc : v.ptr0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,7 +129,7 @@ class ppu_decoder
|
||||
{
|
||||
for (u32 i = 0; i < 1u << 11; i++)
|
||||
{
|
||||
m_table.at(i << 6 | v.value) = i & 1 ? v.ptr_rc : v.ptr0;
|
||||
::at32(m_table, i << 6 | v.value) = i & 1 ? v.ptr_rc : v.ptr0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -710,8 +710,8 @@ struct ppu_far_jumps_t
|
||||
Label jmp_address = c.newLabel();
|
||||
Label imm_address = c.newLabel();
|
||||
|
||||
c.ldr(args[1].r32(), arm::ptr(imm_address));
|
||||
c.str(args[1].r32(), arm::Mem(args[0], ::offset32(&ppu_thread::cia)));
|
||||
c.ldr(args[1].w(), arm::ptr(imm_address));
|
||||
c.str(args[1].w(), arm::Mem(args[0], ::offset32(&ppu_thread::cia)));
|
||||
c.ldr(args[1], arm::ptr(jmp_address));
|
||||
c.br(args[1]);
|
||||
|
||||
@@ -721,7 +721,7 @@ struct ppu_far_jumps_t
|
||||
c.bind(imm_address);
|
||||
c.embedUInt32(pc);
|
||||
#endif
|
||||
}, &rt);
|
||||
}, &rt);
|
||||
}
|
||||
|
||||
return it->second.func;
|
||||
@@ -1038,7 +1038,7 @@ void ppu_thread::dump_regs(std::string& ret) const
|
||||
return addr % 4 == 0 && vm::check_addr(addr, vm::page_executable) && g_ppu_itype.decode(*vm::get_super_ptr<u32>(addr)) != ppu_itype::UNK;
|
||||
};
|
||||
|
||||
if (const u32 reg_ptr = *vm::get_super_ptr<u32>(static_cast<u32>(reg));
|
||||
if (const u32 reg_ptr = *vm::get_super_ptr<be_t<u32, 1>>(static_cast<u32>(reg));
|
||||
vm::check_addr<8>(reg_ptr) && !vm::check_addr(toc, vm::page_executable))
|
||||
{
|
||||
// Check executability and alignment
|
||||
@@ -1277,6 +1277,16 @@ std::string ppu_thread::dump_misc() const
|
||||
{
|
||||
std::string ret = cpu_thread::dump_misc();
|
||||
|
||||
if (ack_suspend)
|
||||
{
|
||||
if (ret.ends_with("\n"))
|
||||
{
|
||||
ret.pop_back();
|
||||
}
|
||||
|
||||
fmt::append(ret, " (LV2 suspended)\n");
|
||||
}
|
||||
|
||||
fmt::append(ret, "Priority: %d\n", +prio);
|
||||
fmt::append(ret, "Stack: 0x%x..0x%x\n", stack_addr, stack_addr + stack_size - 1);
|
||||
fmt::append(ret, "Joiner: %s\n", joiner.load());
|
||||
@@ -1296,7 +1306,7 @@ std::string ppu_thread::dump_misc() const
|
||||
if (u64 v = gpr[i]; v != syscall_args[i - 3])
|
||||
fmt::append(ret, " ** r%d: 0x%llx\n", i, v);
|
||||
}
|
||||
else if (is_paused())
|
||||
else if (is_paused() || is_stopped())
|
||||
{
|
||||
if (const auto last_func = last_function)
|
||||
{
|
||||
@@ -1401,7 +1411,7 @@ void ppu_thread::cpu_task()
|
||||
}
|
||||
case ppu_cmd::hle_call:
|
||||
{
|
||||
cmd_pop(), ppu_function_manager::get().at(arg)(*this, {arg}, vm::_ptr<u32>(cia - 4), &ppu_ret);
|
||||
cmd_pop(), ::at32(ppu_function_manager::get(), arg)(*this, {arg}, vm::_ptr<u32>(cia - 4), &ppu_ret);
|
||||
break;
|
||||
}
|
||||
case ppu_cmd::opd_call:
|
||||
@@ -1712,7 +1722,7 @@ ppu_thread::ppu_thread(utils::serial& ar)
|
||||
{
|
||||
cmd_list
|
||||
({
|
||||
{ppu_cmd::ptr_call, 0}, +[](ppu_thread& ppu) -> bool
|
||||
{ppu_cmd::ptr_call, 0}, +[](ppu_thread&) -> bool
|
||||
{
|
||||
while (!Emu.IsStopped() && !g_fxo->get<init_pushed>().inited)
|
||||
{
|
||||
@@ -1799,7 +1809,7 @@ void ppu_thread::save(utils::serial& ar)
|
||||
if (_joiner >= ppu_join_status::max)
|
||||
{
|
||||
// Joining thread should recover this member properly
|
||||
_joiner = ppu_join_status::joinable;
|
||||
_joiner = ppu_join_status::joinable;
|
||||
}
|
||||
|
||||
if (state & cpu_flag::again)
|
||||
@@ -1943,6 +1953,13 @@ void ppu_thread::fast_call(u32 addr, u64 rtoc)
|
||||
return fmt::format("PPU[0x%x] Thread (%s) [HLE:0x%08x, LR:0x%08x]", _this->id, *name_cache.get(), cia, _this->lr);
|
||||
}
|
||||
|
||||
extern const char* get_prx_name_by_cia(u32 addr);
|
||||
|
||||
if (auto name = get_prx_name_by_cia(cia))
|
||||
{
|
||||
return fmt::format("PPU[0x%x] Thread (%s) [%s: 0x%08x]", _this->id, *name_cache.get(), name, cia);
|
||||
}
|
||||
|
||||
return fmt::format("PPU[0x%x] Thread (%s) [0x%08x]", _this->id, *name_cache.get(), cia);
|
||||
};
|
||||
|
||||
@@ -2642,11 +2659,9 @@ static bool ppu_store_reservation(ppu_thread& ppu, u32 addr, u64 reg_value)
|
||||
}())
|
||||
{
|
||||
// Test a common pattern in lwmutex
|
||||
constexpr u64 mutex_free = u64{static_cast<u32>(0 - 1)} << 32;
|
||||
const bool may_be_lwmutex_related = sizeof(T) == 8 ?
|
||||
(new_data == mutex_free && old_data == u64{ppu.id} << 32) : (old_data == mutex_free && new_data == u64{ppu.id} << 32);
|
||||
extern atomic_t<u32> liblv2_begin, liblv2_end;
|
||||
|
||||
if (!may_be_lwmutex_related)
|
||||
if (ppu.cia < liblv2_begin || ppu.cia >= liblv2_end)
|
||||
{
|
||||
res.notify_all(-128);
|
||||
}
|
||||
@@ -2890,7 +2905,7 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
||||
std::string upper = fmt::to_upper(entry.name);
|
||||
|
||||
// Skip already loaded modules or HLEd ones
|
||||
auto is_ignored = [&](s64 offset) -> bool
|
||||
auto is_ignored = [&](s64 /*offset*/) -> bool
|
||||
{
|
||||
if (dir_queue[i] != firmware_sprx_path)
|
||||
{
|
||||
@@ -2922,7 +2937,7 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
|
||||
extern const std::map<std::string_view, int> g_prx_list;
|
||||
|
||||
// Use list
|
||||
return g_prx_list.count(entry.name) && g_prx_list.at(entry.name) != 0;
|
||||
return g_prx_list.count(entry.name) && ::at32(g_prx_list, entry.name) != 0;
|
||||
};
|
||||
|
||||
// Check .sprx filename
|
||||
@@ -3280,6 +3295,11 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
|
||||
{
|
||||
std::unordered_map<std::string, u64> link_table
|
||||
{
|
||||
{ "sys_game_watchdog_start", reinterpret_cast<u64>(ppu_execute_syscall) },
|
||||
{ "sys_game_watchdog_stop", reinterpret_cast<u64>(ppu_execute_syscall) },
|
||||
{ "sys_game_watchdog_clear", reinterpret_cast<u64>(ppu_execute_syscall) },
|
||||
{ "sys_game_get_system_sw_version", reinterpret_cast<u64>(ppu_execute_syscall) },
|
||||
{ "sys_game_board_storage_read", reinterpret_cast<u64>(ppu_execute_syscall) },
|
||||
{ "__trap", reinterpret_cast<u64>(&ppu_trap) },
|
||||
{ "__error", reinterpret_cast<u64>(&ppu_error) },
|
||||
{ "__check", reinterpret_cast<u64>(&ppu_check) },
|
||||
@@ -3369,7 +3389,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
|
||||
usz fpos = 0;
|
||||
|
||||
// Difference between function name and current location
|
||||
const u32 reloc = info.relocs.empty() ? 0 : info.segs.at(0).addr;
|
||||
const u32 reloc = info.relocs.empty() ? 0 : ::at32(info.segs, 0).addr;
|
||||
|
||||
// Info sent to threads
|
||||
std::vector<std::pair<std::string, ppu_module>> workload;
|
||||
@@ -3725,6 +3745,11 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
|
||||
// Allocate "core"
|
||||
std::lock_guard jlock(g_fxo->get<jit_core_allocator>().sem);
|
||||
|
||||
if (Emu.IsStopped())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ppu_log.warning("LLVM: Compiling module %s%s", cache_path, obj_name);
|
||||
|
||||
// Use another JIT instance
|
||||
|
||||
@@ -534,7 +534,7 @@ Value* PPUTranslator::Shuffle(Value* left, Value* right, std::initializer_list<u
|
||||
// Transform indices (works for vectors with size 2^N)
|
||||
for (usz i = 0; i < indices.size(); i++)
|
||||
{
|
||||
data.push_back(indices.end()[~i] ^ mask);
|
||||
data.push_back(*(indices.begin() + indices.size() - 1 - i) ^ mask);
|
||||
}
|
||||
|
||||
return m_ir->CreateShuffleVector(left, right, ConstantDataVector::get(m_context, data));
|
||||
@@ -1289,7 +1289,7 @@ void PPUTranslator::VPERM(ppu_opcode_t op)
|
||||
if (m_use_avx512_icl)
|
||||
{
|
||||
const auto i = eval(~c);
|
||||
set_vr(op.vd, vperm2b256to128(b, a, i));
|
||||
set_vr(op.vd, vperm2b(b, a, i));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ bool spu_thread::write_reg(const u32 addr, const u32 value)
|
||||
|
||||
case SPU_In_MBox_offs:
|
||||
{
|
||||
ch_in_mbox.push(*this, value);
|
||||
ch_in_mbox.push(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -323,6 +323,8 @@ void spu_load_exec(const spu_exec_object& elf)
|
||||
spu_thread::g_raw_spu_ctr++;
|
||||
|
||||
auto spu = idm::make_ptr<named_thread<spu_thread>>(nullptr, 0, "test_spu", 0);
|
||||
ensure(vm::get(vm::spu)->falloc(spu->vm_offset(), SPU_LS_SIZE, &spu->shm, vm::page_size_64k));
|
||||
spu->map_ls(*spu->shm, spu->ls);
|
||||
|
||||
for (const auto& prog : elf.progs)
|
||||
{
|
||||
@@ -343,6 +345,8 @@ void spu_load_rel_exec(const spu_rel_object& elf)
|
||||
spu_thread::g_raw_spu_ctr++;
|
||||
|
||||
auto spu = idm::make_ptr<named_thread<spu_thread>>(nullptr, 0, "test_spu", 0);
|
||||
ensure(vm::get(vm::spu)->falloc(spu->vm_offset(), SPU_LS_SIZE, &spu->shm, vm::page_size_64k));
|
||||
spu->map_ls(*spu->shm, spu->ls);
|
||||
|
||||
u64 total_memsize = 0;
|
||||
|
||||
|
||||
@@ -675,7 +675,7 @@ spu_function_t spu_recompiler::compile(spu_program&& _func)
|
||||
}
|
||||
|
||||
// Determine which value will be duplicated at hole positions
|
||||
const u32 w3 = func.data.at((j - start + ~static_cast<u32>(std::countl_zero(cmask)) % 4 * 4) / 4);
|
||||
const u32 w3 = ::at32(func.data, (j - start + ~static_cast<u32>(std::countl_zero(cmask)) % 4 * 4) / 4);
|
||||
words.push_back(cmask & 1 ? func.data[(j - start + 0) / 4] : w3);
|
||||
words.push_back(cmask & 2 ? func.data[(j - start + 4) / 4] : w3);
|
||||
words.push_back(cmask & 4 ? func.data[(j - start + 8) / 4] : w3);
|
||||
|
||||
@@ -291,7 +291,7 @@ struct spu_itype
|
||||
// Test for constant loading instruction
|
||||
friend constexpr bool operator &(type value, constant_tag)
|
||||
{
|
||||
return value >= CBD && value <= FSMBI;
|
||||
return value >= ILH && value <= FSMBI;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+234
-154
@@ -28,10 +28,6 @@
|
||||
#include "util/simd.hpp"
|
||||
#include "util/sysinfo.hpp"
|
||||
|
||||
#if defined(ARCH_ARM64)
|
||||
#include "Emu/CPU/sse2neon.h"
|
||||
#endif
|
||||
|
||||
const extern spu_decoder<spu_itype> g_spu_itype;
|
||||
const extern spu_decoder<spu_iname> g_spu_iname;
|
||||
const extern spu_decoder<spu_iflag> g_spu_iflag;
|
||||
@@ -974,8 +970,8 @@ spu_function_t spu_runtime::rebuild_ubertrampoline(u32 id_inst)
|
||||
static thread_local std::vector<std::pair<std::basic_string_view<u32>, spu_function_t>> m_flat_list;
|
||||
|
||||
// Remember top position
|
||||
auto stuff_it = m_stuff.at(id_inst >> 12).begin();
|
||||
auto stuff_end = m_stuff.at(id_inst >> 12).end();
|
||||
auto stuff_it = ::at32(m_stuff, id_inst >> 12).begin();
|
||||
auto stuff_end = ::at32(m_stuff, id_inst >> 12).end();
|
||||
{
|
||||
if (stuff_it->trampoline)
|
||||
{
|
||||
@@ -1038,43 +1034,73 @@ spu_function_t spu_runtime::rebuild_ubertrampoline(u32 id_inst)
|
||||
|
||||
auto make_jump = [&](asmjit::arm::CondCode op, auto target)
|
||||
{
|
||||
// 36 bytes
|
||||
// Fallback to dispatch if no target
|
||||
const u64 taddr = target ? reinterpret_cast<u64>(target) : reinterpret_cast<u64>(tr_dispatch);
|
||||
|
||||
// Build with asmjit to make things more readable
|
||||
// Might cost some RAM, but meh whatever
|
||||
auto temp = build_function_asm<spu_function_t>("", [&](native_asm& c, auto& args)
|
||||
// ldr x9, #16 -> ldr x9, taddr
|
||||
*raw++ = 0x89;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x58;
|
||||
|
||||
if (op == asmjit::arm::CondCode::kAlways)
|
||||
{
|
||||
// br x9
|
||||
*raw++ = 0x20;
|
||||
*raw++ = 0x01;
|
||||
*raw++ = 0x1F;
|
||||
*raw++ = 0xD6;
|
||||
|
||||
// nop
|
||||
*raw++ = 0x1F;
|
||||
*raw++ = 0x20;
|
||||
*raw++ = 0x03;
|
||||
*raw++ = 0xD5;
|
||||
|
||||
// nop
|
||||
*raw++ = 0x1F;
|
||||
*raw++ = 0x20;
|
||||
*raw++ = 0x03;
|
||||
*raw++ = 0xD5;
|
||||
}
|
||||
else
|
||||
{
|
||||
// b.COND #8 -> b.COND do_branch
|
||||
switch (op)
|
||||
{
|
||||
using namespace asmjit;
|
||||
case asmjit::arm::CondCode::kUnsignedLT:
|
||||
*raw++ = 0x43;
|
||||
break;
|
||||
case asmjit::arm::CondCode::kUnsignedGT:
|
||||
*raw++ = 0x48;
|
||||
break;
|
||||
default:
|
||||
asm("brk 0x42");
|
||||
}
|
||||
|
||||
c.movk(a64::x9, Imm(static_cast<u16>(taddr >> 48)), Imm(48));
|
||||
c.movk(a64::x9, Imm(static_cast<u16>(taddr >> 32)), Imm(32));
|
||||
c.movk(a64::x9, Imm(static_cast<u16>(taddr >> 16)), Imm(16));
|
||||
c.movk(a64::x9, Imm(static_cast<u16>(taddr)), Imm(0));
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x54;
|
||||
|
||||
if (op == arm::CondCode::kAlways)
|
||||
{
|
||||
c.br(a64::x9);
|
||||
// Constant length per jmp for easier stub patching
|
||||
c.nop();
|
||||
c.nop();
|
||||
} else
|
||||
{
|
||||
Label do_branch = c.newLabel();
|
||||
Label cont = c.newLabel();
|
||||
// b #16 -> b cont
|
||||
*raw++ = 0x04;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x14;
|
||||
|
||||
c.b(op, do_branch);
|
||||
c.b(cont);
|
||||
// do_branch: br x9
|
||||
*raw++ = 0x20;
|
||||
*raw++ = 0x01;
|
||||
*raw++ = 0x1f;
|
||||
*raw++ = 0xD6;
|
||||
}
|
||||
|
||||
c.bind(do_branch);
|
||||
c.br(a64::x9);
|
||||
// taddr
|
||||
std::memcpy(raw, &taddr, 8);
|
||||
raw += 8;
|
||||
|
||||
c.bind(cont);
|
||||
}
|
||||
});
|
||||
u8 mem_used = 7 * 4;
|
||||
memcpy(raw, reinterpret_cast<u8*>(temp), mem_used);
|
||||
raw += mem_used;
|
||||
// cont: next instruction
|
||||
};
|
||||
#elif defined(ARCH_X64)
|
||||
// Allocate some writable executable memory
|
||||
@@ -1152,7 +1178,7 @@ spu_function_t spu_runtime::rebuild_ubertrampoline(u32 id_inst)
|
||||
break;
|
||||
}
|
||||
|
||||
const u32 x1 = w.beg->first.at(w.level);
|
||||
const u32 x1 = ::at32(w.beg->first, w.level);
|
||||
|
||||
if (!x1)
|
||||
{
|
||||
@@ -1175,7 +1201,7 @@ spu_function_t spu_runtime::rebuild_ubertrampoline(u32 id_inst)
|
||||
}
|
||||
|
||||
// Adjust ranges (forward)
|
||||
while (it != w.end && x1 == it->first.at(w.level))
|
||||
while (it != w.end && x1 == ::at32(it->first, w.level))
|
||||
{
|
||||
it++;
|
||||
size1++;
|
||||
@@ -1203,17 +1229,7 @@ spu_function_t spu_runtime::rebuild_ubertrampoline(u32 id_inst)
|
||||
// Rewrite jump address
|
||||
{
|
||||
u64 raw64 = reinterpret_cast<u64>(raw);
|
||||
auto temp = build_function_asm<spu_function_t>("", [&](native_asm& c, auto& args)
|
||||
{
|
||||
using namespace asmjit;
|
||||
|
||||
c.movk(a64::x9, Imm(static_cast<u16>(raw64 >> 48)), Imm(48));
|
||||
c.movk(a64::x9, Imm(static_cast<u16>(raw64 >> 32)), Imm(32));
|
||||
c.movk(a64::x9, Imm(static_cast<u16>(raw64 >> 16)), Imm(16));
|
||||
c.movk(a64::x9, Imm(static_cast<u16>(raw64)), Imm(0));
|
||||
});
|
||||
|
||||
memcpy(w.rel32 - (4 * 7), reinterpret_cast<u8*>(temp), 4 * 4);
|
||||
memcpy(w.rel32 - 8, &raw64, 8);
|
||||
}
|
||||
#else
|
||||
#error "Unimplemented"
|
||||
@@ -1236,7 +1252,7 @@ spu_function_t spu_runtime::rebuild_ubertrampoline(u32 id_inst)
|
||||
}
|
||||
|
||||
// Value for comparison
|
||||
const u32 x = it->first.at(w.level);
|
||||
const u32 x = ::at32(it->first, w.level);
|
||||
|
||||
// Adjust ranges (backward)
|
||||
while (it != m_flat_list.begin())
|
||||
@@ -1249,7 +1265,7 @@ spu_function_t spu_runtime::rebuild_ubertrampoline(u32 id_inst)
|
||||
break;
|
||||
}
|
||||
|
||||
if (it->first.at(w.level) != x)
|
||||
if (::at32(it->first, w.level) != x)
|
||||
{
|
||||
it++;
|
||||
break;
|
||||
@@ -1302,19 +1318,27 @@ spu_function_t spu_runtime::rebuild_ubertrampoline(u32 id_inst)
|
||||
raw += 4;
|
||||
}
|
||||
#elif defined(ARCH_ARM64)
|
||||
{
|
||||
auto temp = build_function_asm<spu_function_t>("", [&](native_asm& c, auto& args)
|
||||
{
|
||||
using namespace asmjit;
|
||||
// ldr w9, #8
|
||||
*raw++ = 0x49;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x18;
|
||||
|
||||
c.movz(a64::w9, Imm(static_cast<u16>(cmp_lsa >> 16)), Imm(16));
|
||||
c.movk(a64::w9, Imm(static_cast<u16>(cmp_lsa)), Imm(0));
|
||||
c.ldr(a64::w1, arm::Mem(a64::x7, a64::x9));
|
||||
});
|
||||
// b #8
|
||||
*raw++ = 0x02;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x14;
|
||||
|
||||
memcpy(raw, reinterpret_cast<u8*>(temp), 3 * 4);
|
||||
raw += 3 * 4;
|
||||
}
|
||||
// cmp_lsa
|
||||
std::memcpy(raw, &cmp_lsa, 4);
|
||||
raw += 4;
|
||||
|
||||
// ldr w1, [x7, x9]
|
||||
*raw++ = 0xE1;
|
||||
*raw++ = 0x68;
|
||||
*raw++ = 0x69;
|
||||
*raw++ = 0xB8;
|
||||
#else
|
||||
#error "Unimplemented"
|
||||
#endif
|
||||
@@ -1326,19 +1350,27 @@ spu_function_t spu_runtime::rebuild_ubertrampoline(u32 id_inst)
|
||||
std::memcpy(raw, &x, 4);
|
||||
raw += 4;
|
||||
#elif defined(ARCH_ARM64)
|
||||
{
|
||||
auto temp = build_function_asm<spu_function_t>("", [&](native_asm& c, auto& args)
|
||||
{
|
||||
using namespace asmjit;
|
||||
// ldr w9, #8
|
||||
*raw++ = 0x49;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x18;
|
||||
|
||||
c.movz(a64::w9, Imm(static_cast<u16>(x >> 16)), Imm(16));
|
||||
c.movk(a64::w9, Imm(static_cast<u16>(x)), Imm(0));
|
||||
c.cmp(a64::w1, a64::w9);
|
||||
});
|
||||
// b #8
|
||||
*raw++ = 0x02;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x14;
|
||||
|
||||
memcpy(raw, reinterpret_cast<u8*>(temp), 3 * 4);
|
||||
raw += 3 * 4;
|
||||
}
|
||||
// x
|
||||
std::memcpy(raw, &x, 4);
|
||||
raw += 4;
|
||||
|
||||
// cmp w1, w9
|
||||
*raw++ = 0x3f;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x09;
|
||||
*raw++ = 0x6B;
|
||||
#else
|
||||
#error "Unimplemented"
|
||||
#endif
|
||||
@@ -1388,7 +1420,7 @@ spu_function_t spu_runtime::rebuild_ubertrampoline(u32 id_inst)
|
||||
it2 = it;
|
||||
|
||||
// Select additional midrange for equality comparison
|
||||
while (it2 != w.end && it2->first.at(w.level) == x)
|
||||
while (it2 != w.end && ::at32(it2->first, w.level) == x)
|
||||
{
|
||||
size2--;
|
||||
it2++;
|
||||
@@ -1486,7 +1518,7 @@ spu_function_t spu_runtime::rebuild_ubertrampoline(u32 id_inst)
|
||||
}
|
||||
|
||||
// Install ubertrampoline
|
||||
auto& insert_to = spu_runtime::g_dispatcher->at(id_inst >> 12);
|
||||
auto& insert_to = ::at32(*spu_runtime::g_dispatcher, id_inst >> 12);
|
||||
|
||||
auto _old = insert_to.load();
|
||||
|
||||
@@ -1519,7 +1551,7 @@ spu_function_t spu_runtime::rebuild_ubertrampoline(u32 id_inst)
|
||||
|
||||
spu_function_t spu_runtime::find(const u32* ls, u32 addr) const
|
||||
{
|
||||
for (auto& item : m_stuff.at(ls[addr / 4] >> 12))
|
||||
for (auto& item : ::at32(m_stuff, ls[addr / 4] >> 12))
|
||||
{
|
||||
if (const auto ptr = item.compiled.load())
|
||||
{
|
||||
@@ -1573,34 +1605,56 @@ spu_function_t spu_runtime::make_branch_patchpoint(u16 data) const
|
||||
|
||||
return reinterpret_cast<spu_function_t>(raw);
|
||||
#elif defined(ARCH_ARM64)
|
||||
spu_function_t func = build_function_asm<spu_function_t>("", [&](native_asm& c, auto& args)
|
||||
{
|
||||
using namespace asmjit;
|
||||
#if defined(__APPLE__)
|
||||
pthread_jit_write_protect_np(false);
|
||||
#endif
|
||||
|
||||
// Save the jmp addr to GHC CC 3rd arg -> REG_Hp
|
||||
Label replace_addr = c.newLabel();
|
||||
u8* const patch_fn = ensure(jit_runtime::alloc(36, 16));
|
||||
u8* raw = patch_fn;
|
||||
|
||||
c.adr(a64::x21, replace_addr);
|
||||
// 16 byte alignment for the jump replacement
|
||||
c.nop();
|
||||
c.nop();
|
||||
c.nop();
|
||||
Label branch_target = c.newLabel();
|
||||
// adr x21, #16
|
||||
*raw++ = 0x95;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x10;
|
||||
|
||||
c.bind(replace_addr);
|
||||
c.ldr(a64::x9, arm::Mem(branch_target));
|
||||
c.br(a64::x9);
|
||||
// nop x3
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
*raw++ = 0x1F;
|
||||
*raw++ = 0x20;
|
||||
*raw++ = 0x03;
|
||||
*raw++ = 0xD5;
|
||||
}
|
||||
|
||||
c.bind(branch_target);
|
||||
c.embedUInt64(reinterpret_cast<u64>(tr_branch));
|
||||
// ldr x9, #8
|
||||
*raw++ = 0x49;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x00;
|
||||
*raw++ = 0x58;
|
||||
|
||||
c.embedUInt8(data >> 8);
|
||||
c.embedUInt8(data & 0xff);
|
||||
// br x9
|
||||
*raw++ = 0x20;
|
||||
*raw++ = 0x01;
|
||||
*raw++ = 0x1F;
|
||||
*raw++ = 0xD6;
|
||||
|
||||
c.embed("branch_patchpoint", 17);
|
||||
});
|
||||
u64 branch_target = reinterpret_cast<u64>(tr_branch);
|
||||
std::memcpy(raw, &branch_target, 8);
|
||||
raw += 8;
|
||||
|
||||
return func;
|
||||
*raw++ = static_cast<u8>(data >> 8);
|
||||
*raw++ = static_cast<u8>(data & 0xff);
|
||||
|
||||
#if defined(__APPLE__)
|
||||
pthread_jit_write_protect_np(true);
|
||||
#endif
|
||||
|
||||
// Flush all cache lines after potentially writing executable code
|
||||
asm("ISB");
|
||||
asm("DSB ISH");
|
||||
|
||||
return reinterpret_cast<spu_function_t>(patch_fn);
|
||||
#else
|
||||
#error "Unimplemented"
|
||||
#endif
|
||||
@@ -1636,18 +1690,26 @@ void spu_recompiler_base::dispatch(spu_thread& spu, void*, u8* rip)
|
||||
|
||||
atomic_storage<u64>::release(*reinterpret_cast<u64*>(rip - 8), result);
|
||||
#elif defined(ARCH_ARM64)
|
||||
auto jump_instrs = build_function_asm<spu_function_t>("", [](native_asm& c, auto& args)
|
||||
{
|
||||
using namespace asmjit;
|
||||
union
|
||||
{
|
||||
u8 bytes[16];
|
||||
u128 result;
|
||||
};
|
||||
|
||||
Label branch_target = c.newLabel();
|
||||
c.ldr(a64::x9, arm::Mem(branch_target)); // PC rel load
|
||||
c.br(a64::x9);
|
||||
// ldr x9, #8
|
||||
bytes[0] = 0x49;
|
||||
bytes[1] = 0x00;
|
||||
bytes[2] = 0x00;
|
||||
bytes[3] = 0x58;
|
||||
|
||||
c.bind(branch_target);
|
||||
c.embedUInt64(reinterpret_cast<u64>(spu_runtime::tr_all));
|
||||
});
|
||||
u128 result = *reinterpret_cast<u128*>(jump_instrs);
|
||||
// br x9
|
||||
bytes[4] = 0x20;
|
||||
bytes[5] = 0x01;
|
||||
bytes[6] = 0x1F;
|
||||
bytes[7] = 0xD6;
|
||||
|
||||
const u64 target = reinterpret_cast<u64>(spu_runtime::tr_all);
|
||||
std::memcpy(bytes + 8, &target, 8);
|
||||
#if defined(__APPLE__)
|
||||
pthread_jit_write_protect_np(false);
|
||||
#endif
|
||||
@@ -1665,7 +1727,7 @@ void spu_recompiler_base::dispatch(spu_thread& spu, void*, u8* rip)
|
||||
}
|
||||
|
||||
// Second attempt (recover from the recursion after repeated unsuccessful trampoline call)
|
||||
if (spu.block_counter != spu.block_recover && &dispatch != spu_runtime::g_dispatcher->at(spu._ref<nse_t<u32>>(spu.pc) >> 12))
|
||||
if (spu.block_counter != spu.block_recover && &dispatch != ::at32(*spu_runtime::g_dispatcher, spu._ref<nse_t<u32>>(spu.pc) >> 12))
|
||||
{
|
||||
spu.block_recover = spu.block_counter;
|
||||
return;
|
||||
@@ -1768,18 +1830,26 @@ void spu_recompiler_base::branch(spu_thread& spu, void*, u8* rip)
|
||||
|
||||
atomic_storage<u64>::release(*reinterpret_cast<u64*>(rip), result);
|
||||
#elif defined(ARCH_ARM64)
|
||||
auto jmp_instrs = build_function_asm<spu_function_t>("", [&](native_asm& c, auto& args)
|
||||
{
|
||||
using namespace asmjit;
|
||||
union
|
||||
{
|
||||
u8 bytes[16];
|
||||
u128 result;
|
||||
};
|
||||
|
||||
Label branch_target = c.newLabel();
|
||||
c.ldr(a64::x9, arm::Mem(branch_target)); // PC rel load
|
||||
c.br(a64::x9);
|
||||
// ldr x9, #8
|
||||
bytes[0] = 0x49;
|
||||
bytes[1] = 0x00;
|
||||
bytes[2] = 0x00;
|
||||
bytes[3] = 0x58;
|
||||
|
||||
c.bind(branch_target);
|
||||
c.embedUInt64(reinterpret_cast<u64>(func));
|
||||
});
|
||||
u128 result = *reinterpret_cast<u128*>(jmp_instrs);
|
||||
// br x9
|
||||
bytes[4] = 0x20;
|
||||
bytes[5] = 0x01;
|
||||
bytes[6] = 0x1F;
|
||||
bytes[7] = 0xD6;
|
||||
|
||||
const u64 target = reinterpret_cast<u64>(func);
|
||||
std::memcpy(bytes + 8, &target, 8);
|
||||
#if defined(__APPLE__)
|
||||
pthread_jit_write_protect_np(false);
|
||||
#endif
|
||||
@@ -2629,7 +2699,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
// Check for possible fallthrough predecessor
|
||||
if (!had_fallthrough)
|
||||
{
|
||||
if (result.data.at((j - lsa) / 4 - 1) == 0 || m_targets.count(j - 4))
|
||||
if (::at32(result.data, (j - lsa) / 4 - 1) == 0 || m_targets.count(j - 4))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -3014,13 +3084,14 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
{
|
||||
workload.clear();
|
||||
workload.push_back(entry_point);
|
||||
ensure(m_bbs.count(entry_point));
|
||||
|
||||
std::basic_string<u32> new_entries;
|
||||
|
||||
for (u32 wi = 0; wi < workload.size(); wi++)
|
||||
{
|
||||
const u32 addr = workload[wi];
|
||||
auto& block = m_bbs.at(addr);
|
||||
auto& block = ::at32(m_bbs, addr);
|
||||
const u32 _new = block.chunk;
|
||||
|
||||
if (!m_entry_info[addr / 4])
|
||||
@@ -3028,7 +3099,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
// Check block predecessors
|
||||
for (u32 pred : block.preds)
|
||||
{
|
||||
const u32 _old = m_bbs.at(pred).chunk;
|
||||
const u32 _old = ::at32(m_bbs, pred).chunk;
|
||||
|
||||
if (_old < 0x40000 && _old != _new)
|
||||
{
|
||||
@@ -3097,9 +3168,9 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
// Fill workload adding targets
|
||||
for (u32 wi = 0; wi < workload.size(); wi++)
|
||||
{
|
||||
const u32 addr = workload[wi];
|
||||
auto& block = m_bbs.at(addr);
|
||||
block.analysed = true;
|
||||
const u32 addr = workload[wi];
|
||||
auto& block = ::at32(m_bbs, addr);
|
||||
block.analysed = true;
|
||||
|
||||
for (u32 target : block.targets)
|
||||
{
|
||||
@@ -3141,7 +3212,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
for (u32 wi = 0; wi < workload.size(); wi++)
|
||||
{
|
||||
const u32 addr = workload[wi];
|
||||
auto& block = m_bbs.at(addr);
|
||||
auto& block = ::at32(m_bbs, addr);
|
||||
|
||||
// Initialize entry point with default value: unknown origin (requires load)
|
||||
if (m_entry_info[addr / 4])
|
||||
@@ -3231,7 +3302,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
for (u32 wi = 0; wi < workload.size(); wi++)
|
||||
{
|
||||
const u32 addr = workload[wi];
|
||||
auto& block = m_bbs.at(addr);
|
||||
auto& block = ::at32(m_bbs, addr);
|
||||
|
||||
// Reset values for the next attempt (keep negative values)
|
||||
for (u32 i = 0; i < s_reg_max; i++)
|
||||
@@ -3253,8 +3324,8 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
}
|
||||
|
||||
const u32 addr = workload[wi];
|
||||
auto& bb = m_bbs.at(addr);
|
||||
auto& func = m_funcs.at(bb.func);
|
||||
auto& bb = ::at32(m_bbs, addr);
|
||||
auto& func = ::at32(m_funcs, bb.func);
|
||||
|
||||
// Update function size
|
||||
func.size = std::max<u16>(func.size, bb.size + (addr - bb.func) / 4);
|
||||
@@ -3266,7 +3337,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
|
||||
if (orig < 0x40000)
|
||||
{
|
||||
auto& src = m_bbs.at(orig);
|
||||
auto& src = ::at32(m_bbs, orig);
|
||||
bb.reg_const[i] = src.reg_const[i];
|
||||
bb.reg_val32[i] = src.reg_val32[i];
|
||||
}
|
||||
@@ -3280,7 +3351,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
|
||||
if (u32 orig = bb.reg_origin_abs[s_reg_sp]; orig < 0x40000)
|
||||
{
|
||||
auto& prologue = m_bbs.at(orig);
|
||||
auto& prologue = ::at32(m_bbs, orig);
|
||||
|
||||
// Copy stack offset (from the assumed prologue)
|
||||
bb.stack_sub = prologue.stack_sub;
|
||||
@@ -3291,7 +3362,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
bb.stack_sub = 0x80000000;
|
||||
}
|
||||
|
||||
spu_opcode_t op;
|
||||
spu_opcode_t op{};
|
||||
|
||||
auto last_inst = spu_itype::UNK;
|
||||
|
||||
@@ -3341,7 +3412,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
}
|
||||
case spu_itype::OR:
|
||||
{
|
||||
bb.reg_const[op.rt] = bb.reg_const[op.ra] & bb.reg_const[op.rb];
|
||||
bb.reg_const[op.rt] = bb.reg_const[op.ra] && bb.reg_const[op.rb];
|
||||
bb.reg_val32[op.rt] = bb.reg_val32[op.ra] | bb.reg_val32[op.rb];
|
||||
break;
|
||||
}
|
||||
@@ -3353,7 +3424,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
}
|
||||
case spu_itype::A:
|
||||
{
|
||||
bb.reg_const[op.rt] = bb.reg_const[op.ra] & bb.reg_const[op.rb];
|
||||
bb.reg_const[op.rt] = bb.reg_const[op.ra] && bb.reg_const[op.rb];
|
||||
bb.reg_val32[op.rt] = bb.reg_val32[op.ra] + bb.reg_val32[op.rb];
|
||||
break;
|
||||
}
|
||||
@@ -3365,7 +3436,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
}
|
||||
case spu_itype::SF:
|
||||
{
|
||||
bb.reg_const[op.rt] = bb.reg_const[op.ra] & bb.reg_const[op.rb];
|
||||
bb.reg_const[op.rt] = bb.reg_const[op.ra] && bb.reg_const[op.rb];
|
||||
bb.reg_val32[op.rt] = bb.reg_val32[op.rb] - bb.reg_val32[op.ra];
|
||||
break;
|
||||
}
|
||||
@@ -3592,8 +3663,8 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
|
||||
for (auto it = m_bbs.lower_bound(f.first); it != m_bbs.end() && it->second.func == f.first; ++it)
|
||||
{
|
||||
auto& bb = it->second;
|
||||
auto& func = m_funcs.at(bb.func);
|
||||
auto& bb = it->second;
|
||||
auto& func = ::at32(m_funcs, bb.func);
|
||||
const u32 addr = it->first;
|
||||
const u32 flim = bb.func + func.size * 4;
|
||||
|
||||
@@ -3609,7 +3680,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
// Check $LR (alternative return registers are currently not supported)
|
||||
if (u32 lr_orig = bb.reg_mod[s_reg_lr] ? addr : bb.reg_origin_abs[s_reg_lr]; lr_orig < 0x40000)
|
||||
{
|
||||
auto& src = m_bbs.at(lr_orig);
|
||||
auto& src = ::at32(m_bbs, lr_orig);
|
||||
|
||||
if (src.reg_load_mod[s_reg_lr] != func.reg_save_off[s_reg_lr])
|
||||
{
|
||||
@@ -3633,7 +3704,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point)
|
||||
{
|
||||
if (u32 orig = bb.reg_mod[i] ? addr : bb.reg_origin_abs[i]; orig < 0x40000)
|
||||
{
|
||||
auto& src = m_bbs.at(orig);
|
||||
auto& src = ::at32(m_bbs, orig);
|
||||
|
||||
if (src.reg_load_mod[i] != func.reg_save_off[i])
|
||||
{
|
||||
@@ -4362,7 +4433,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
|
||||
return m_ir->CreateBitCast(_ptr<u8>(m_thread, get_reg_offset(index)), get_reg_type(index)->getPointerTo());
|
||||
}
|
||||
|
||||
auto& ptr = m_reg_addr.at(index);
|
||||
auto& ptr = ::at32(m_reg_addr, index);
|
||||
|
||||
if (!ptr)
|
||||
{
|
||||
@@ -4472,7 +4543,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
|
||||
{
|
||||
llvm::Value* dummy{};
|
||||
|
||||
auto& reg = *(m_block ? &m_block->reg.at(index) : &dummy);
|
||||
auto& reg = *(m_block ? &::at32(m_block->reg, index) : &dummy);
|
||||
|
||||
if (!reg)
|
||||
{
|
||||
@@ -4556,7 +4627,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
|
||||
|
||||
if (m_block)
|
||||
{
|
||||
auto v = m_block->reg.at(index);
|
||||
auto v = ::at32(m_block->reg, index);
|
||||
|
||||
if (v && v->getType() == get_type<T>())
|
||||
{
|
||||
@@ -4667,7 +4738,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
|
||||
value->setName(fmt::format("result_0x%05x", m_pos));
|
||||
#endif
|
||||
|
||||
m_block->reg.at(index) = saved_value;
|
||||
::at32(m_block->reg, index) = saved_value;
|
||||
}
|
||||
|
||||
// Get register location
|
||||
@@ -5233,7 +5304,7 @@ public:
|
||||
const u32 baddr = m_block_queue[bi];
|
||||
m_block = &m_blocks[baddr];
|
||||
m_ir->SetInsertPoint(m_block->block);
|
||||
auto& bb = m_bbs.at(baddr);
|
||||
auto& bb = ::at32(m_bbs, baddr);
|
||||
bool need_check = false;
|
||||
m_block->bb = &bb;
|
||||
|
||||
@@ -6558,17 +6629,19 @@ public:
|
||||
|
||||
if (auto ci = llvm::dyn_cast<llvm::ConstantInt>(trunc<u8>(val).eval(m_ir)))
|
||||
{
|
||||
if (g_cfg.core.spu_accurate_dma)
|
||||
if (g_cfg.core.mfc_debug)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (u64 cmdh = ci->getZExtValue() & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK | MFC_RESULT_MASK); g_cfg.core.rsx_fifo_accuracy || !g_use_rtm)
|
||||
bool must_use_cpp_functions = !!g_cfg.core.spu_accurate_dma;
|
||||
|
||||
if (u64 cmdh = ci->getZExtValue() & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK | MFC_RESULT_MASK); g_cfg.core.rsx_fifo_accuracy || g_cfg.video.strict_rendering_mode || !g_use_rtm)
|
||||
{
|
||||
// TODO: don't require TSX (current implementation is TSX-only)
|
||||
if (cmdh == MFC_PUT_CMD || cmdh == MFC_SNDSIG_CMD)
|
||||
{
|
||||
break;
|
||||
must_use_cpp_functions = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6668,12 +6741,16 @@ public:
|
||||
m_ir->CreateCondBr(cond, exec, fail, m_md_likely);
|
||||
m_ir->SetInsertPoint(exec);
|
||||
|
||||
const auto mmio = llvm::BasicBlock::Create(m_context, "", m_function);
|
||||
const auto copy = llvm::BasicBlock::Create(m_context, "", m_function);
|
||||
|
||||
// Always use interpreter function for MFC debug option
|
||||
m_ir->CreateCondBr(m_ir->CreateICmpUGE(eal.value, m_ir->getInt32(g_cfg.core.mfc_debug ? 0 : 0xe0000000)), mmio, copy, m_md_unlikely);
|
||||
m_ir->SetInsertPoint(mmio);
|
||||
if (!must_use_cpp_functions)
|
||||
{
|
||||
const auto mmio = llvm::BasicBlock::Create(m_context, "", m_function);
|
||||
m_ir->CreateCondBr(m_ir->CreateICmpUGE(eal.value, m_ir->getInt32(0xe0000000)), mmio, copy, m_md_unlikely);
|
||||
m_ir->SetInsertPoint(mmio);
|
||||
}
|
||||
|
||||
m_ir->CreateStore(ci, spu_ptr<u8>(&spu_thread::ch_mfc_cmd, &spu_mfc_cmd::cmd));
|
||||
call("spu_exec_mfc_cmd", &exec_mfc_cmd, m_thread);
|
||||
m_ir->CreateBr(next);
|
||||
@@ -7383,12 +7460,13 @@ public:
|
||||
set_vr(op.rt, fshl(a, zshuffle(a, 4, 0, 1, 2), b));
|
||||
}
|
||||
|
||||
#if defined(ARCH_X64) || defined(ARCH_ARM64)
|
||||
#if defined(ARCH_X64)
|
||||
static __m128i exec_rotqby(__m128i a, u8 b)
|
||||
{
|
||||
alignas(32) const __m128i buf[2]{a, a};
|
||||
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(reinterpret_cast<const u8*>(buf) + (16 - (b & 0xf))));
|
||||
}
|
||||
#elif defined(ARCH_ARM64)
|
||||
#else
|
||||
#error "Unimplemented"
|
||||
#endif
|
||||
@@ -7398,6 +7476,7 @@ public:
|
||||
const auto a = get_vr<u8[16]>(op.ra);
|
||||
const auto b = get_vr<u8[16]>(op.rb);
|
||||
|
||||
#if defined(ARCH_X64)
|
||||
if (!m_use_ssse3)
|
||||
{
|
||||
value_t<u8[16]> r;
|
||||
@@ -7405,6 +7484,7 @@ public:
|
||||
set_vr(op.rt, r);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Data with swapped endian from a load instruction
|
||||
if (auto [ok, as] = match_expr(a, byteswap(match<u8[16]>())); ok)
|
||||
@@ -8239,13 +8319,13 @@ public:
|
||||
{
|
||||
if (perm_only)
|
||||
{
|
||||
set_vr(op.rt4, vperm2b256to128(as, bs, c));
|
||||
set_vr(op.rt4, vperm2b(as, bs, c));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto m = gf2p8affineqb(c, build<u8[16]>(0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20), 0x7f);
|
||||
const auto mm = select(noncast<s8[16]>(m) >= 0, splat<u8[16]>(0), m);
|
||||
const auto ab = vperm2b256to128(as, bs, c);
|
||||
const auto ab = vperm2b(as, bs, c);
|
||||
set_vr(op.rt4, select(noncast<s8[16]>(c) >= 0, ab, mm));
|
||||
return;
|
||||
}
|
||||
@@ -8297,18 +8377,18 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (m_use_avx512_icl && (op.ra != op.rb))
|
||||
if (m_use_avx512_icl && (op.ra != op.rb || m_interp_magn))
|
||||
{
|
||||
if (perm_only)
|
||||
{
|
||||
set_vr(op.rt4, vperm2b256to128(a, b, eval(c ^ 0xf)));
|
||||
set_vr(op.rt4, vperm2b(a, b, eval(c ^ 0xf)));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto m = gf2p8affineqb(c, build<u8[16]>(0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20), 0x7f);
|
||||
const auto mm = select(noncast<s8[16]>(m) >= 0, splat<u8[16]>(0), m);
|
||||
const auto cr = eval(c ^ 0xf);
|
||||
const auto ab = vperm2b256to128(a, b, cr);
|
||||
const auto ab = vperm2b(a, b, cr);
|
||||
set_vr(op.rt4, select(noncast<s8[16]>(c) >= 0, ab, mm));
|
||||
return;
|
||||
}
|
||||
@@ -10749,7 +10829,7 @@ struct spu_llvm
|
||||
|
||||
for (auto it = enqueued.begin(), end = enqueued.end(); it != end; ++it)
|
||||
{
|
||||
const u64 cur = std::as_const(samples).at(it->first);
|
||||
const u64 cur = ::at32(std::as_const(samples), it->first);
|
||||
|
||||
if (cur > sample_max)
|
||||
{
|
||||
|
||||
+859
-113
File diff suppressed because it is too large
Load Diff
+51
-125
@@ -231,10 +231,9 @@ public:
|
||||
|
||||
// Turn off waiting bit manually (must succeed because waiting bit can only be resetted by the thread pushed to jostling_value)
|
||||
ensure(this->data.bit_test_reset(off_wait));
|
||||
data.notify_one();
|
||||
}
|
||||
|
||||
data.notify_one();
|
||||
|
||||
// Return true if count has changed from 0 to 1, this condition is considered satisfied even if we pushed a value directly to the special storage for waiting SPUs
|
||||
return !pushed_to_data || (old & bit_count) == 0;
|
||||
}
|
||||
@@ -298,102 +297,10 @@ public:
|
||||
}
|
||||
|
||||
// Waiting for channel pop state availability, actually popping if specified
|
||||
s64 pop_wait(cpu_thread& spu, bool pop = true)
|
||||
{
|
||||
u64 old = data.fetch_op([&](u64& data)
|
||||
{
|
||||
if (data & bit_count) [[likely]]
|
||||
{
|
||||
if (pop)
|
||||
{
|
||||
data = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
data = bit_wait;
|
||||
jostling_value.release(bit_wait);
|
||||
return true;
|
||||
}).first;
|
||||
|
||||
if (old & bit_count)
|
||||
{
|
||||
return static_cast<u32>(old);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
thread_ctrl::wait_on(data, bit_wait);
|
||||
old = data;
|
||||
|
||||
if (!(old & bit_wait))
|
||||
{
|
||||
return static_cast<u32>(jostling_value);
|
||||
}
|
||||
|
||||
if (spu.is_stopped())
|
||||
{
|
||||
// Abort waiting and test if a value has been received
|
||||
if (u64 v = jostling_value.exchange(0); !(v & bit_wait))
|
||||
{
|
||||
return static_cast<u32>(v);
|
||||
}
|
||||
|
||||
ensure(data.bit_test_reset(off_wait));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
s64 pop_wait(cpu_thread& spu, bool pop = true);
|
||||
|
||||
// Waiting for channel push state availability, actually pushing if specified
|
||||
bool push_wait(cpu_thread& spu, u32 value, bool push = true)
|
||||
{
|
||||
u64 state;
|
||||
data.fetch_op([&](u64& data)
|
||||
{
|
||||
if (data & bit_count) [[unlikely]]
|
||||
{
|
||||
jostling_value.release(push ? value : static_cast<u32>(data));
|
||||
data |= bit_wait;
|
||||
}
|
||||
else if (push)
|
||||
{
|
||||
data = bit_count | value;
|
||||
}
|
||||
else
|
||||
{
|
||||
state = data;
|
||||
return false;
|
||||
}
|
||||
|
||||
state = data;
|
||||
return true;
|
||||
});
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!(state & bit_wait))
|
||||
{
|
||||
if (!push)
|
||||
{
|
||||
data &= ~bit_count;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (spu.is_stopped())
|
||||
{
|
||||
data &= ~bit_wait;
|
||||
return false;
|
||||
}
|
||||
|
||||
thread_ctrl::wait_on(data, state);
|
||||
state = data;
|
||||
}
|
||||
}
|
||||
bool push_wait(cpu_thread& spu, u32 value, bool push = true);
|
||||
|
||||
void set_value(u32 value, bool count = true)
|
||||
{
|
||||
@@ -424,45 +331,61 @@ struct spu_channel_4_t
|
||||
};
|
||||
|
||||
atomic_t<sync_var_t> values;
|
||||
atomic_t<u64> jostling_value;
|
||||
atomic_t<u32> value3;
|
||||
|
||||
public:
|
||||
static constexpr u32 off_wait = 32;
|
||||
static constexpr u64 bit_wait = 1ull << off_wait;
|
||||
|
||||
void clear()
|
||||
{
|
||||
values.release({});
|
||||
}
|
||||
|
||||
// push unconditionally (overwriting latest value), returns true if needs signaling
|
||||
void push(cpu_thread& spu, u32 value)
|
||||
void push(u32 value)
|
||||
{
|
||||
value3.release(value);
|
||||
|
||||
if (values.atomic_op([value](sync_var_t& data) -> bool
|
||||
while (true)
|
||||
{
|
||||
switch (data.count++)
|
||||
value3.release(value);
|
||||
const auto [old, pushed_to_data] = values.fetch_op([&](sync_var_t& data)
|
||||
{
|
||||
case 0: data.value0 = value; break;
|
||||
case 1: data.value1 = value; break;
|
||||
case 2: data.value2 = value; break;
|
||||
default:
|
||||
{
|
||||
data.count = 4;
|
||||
data.value3_inval++; // Ensure the SPU reads the most recent value3 write in try_pop by re-loading
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (data.waiting)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data.waiting)
|
||||
{
|
||||
data.waiting = 0;
|
||||
switch (data.count++)
|
||||
{
|
||||
case 0: data.value0 = value; break;
|
||||
case 1: data.value1 = value; break;
|
||||
case 2: data.value2 = value; break;
|
||||
default:
|
||||
{
|
||||
data.count = 4;
|
||||
data.value3_inval++; // Ensure the SPU reads the most recent value3 write in try_pop by re-loading
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!pushed_to_data)
|
||||
{
|
||||
// Insert the pending value in special storage for waiting SPUs, leave no time in which the channel has data
|
||||
if (!jostling_value.compare_and_swap_test(bit_wait, value))
|
||||
{
|
||||
// Other thread has inserted a value through jostling_value, retry
|
||||
continue;
|
||||
}
|
||||
|
||||
// Turn off waiting bit manually (must succeed because waiting bit can only be resetted by the thread pushing to jostling_value)
|
||||
ensure(atomic_storage<u8>::exchange(values.raw().waiting, 0));
|
||||
values.notify_one();
|
||||
}
|
||||
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
spu.notify();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,15 +406,14 @@ public:
|
||||
data.value1 = data.value2;
|
||||
data.value2 = this->value3;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.waiting = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// Returns [previous count, value] (if aborted 0 count is returned)
|
||||
std::pair<u32, u32> pop_wait(cpu_thread& spu);
|
||||
|
||||
// returns current queue size without modification
|
||||
uint try_read(u32 (&out)[4]) const
|
||||
{
|
||||
@@ -891,7 +813,11 @@ public:
|
||||
|
||||
void fast_call(u32 ls_addr);
|
||||
|
||||
bool capture_local_storage() const;
|
||||
std::array<std::shared_ptr<utils::serial>, 32> rewind_captures; // shared_ptr to avoid header inclusion
|
||||
u8 current_rewind_capture_idx = 0;
|
||||
|
||||
bool capture_state();
|
||||
bool try_load_debug_capture();
|
||||
void wakeup_delay(u32 div = 1) const;
|
||||
|
||||
// Convert specified SPU LS address to a pointer of specified (possibly converted to BE) type
|
||||
@@ -918,7 +844,7 @@ public:
|
||||
return group ? SPU_FAKE_BASE_ADDR + SPU_LS_SIZE * (id & 0xffffff) : RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * index;
|
||||
}
|
||||
|
||||
static u8* map_ls(utils::shm& shm);
|
||||
static u8* map_ls(utils::shm& shm, void* ptr = nullptr);
|
||||
|
||||
// Returns true if reservation existed but was just discovered to be lost
|
||||
// It is safe to use on any address, even if not directly accessed by SPU (so it's slower)
|
||||
|
||||
+85
-10
@@ -13,6 +13,7 @@
|
||||
#include "sys_cond.h"
|
||||
#include "sys_event.h"
|
||||
#include "sys_event_flag.h"
|
||||
#include "sys_game.h"
|
||||
#include "sys_interrupt.h"
|
||||
#include "sys_memory.h"
|
||||
#include "sys_mmapper.h"
|
||||
@@ -50,9 +51,15 @@
|
||||
|
||||
#include <optional>
|
||||
#include <deque>
|
||||
#include "util/tsc.hpp"
|
||||
|
||||
extern std::string ppu_get_syscall_name(u64 code);
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
void set_rsx_yield_flag() noexcept;
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<ppu_syscall_code>::format(std::string& out, u64 arg)
|
||||
{
|
||||
@@ -413,11 +420,11 @@ const std::array<std::pair<ppu_intrp_func_t, std::string_view>, 1024> g_ppu_sysc
|
||||
BIND_SYSC(sys_uart_send), //369 (0x171) ROOT
|
||||
BIND_SYSC(sys_uart_get_params), //370 (0x172) ROOT
|
||||
uns_func, //371 (0x173) UNS
|
||||
NULL_FUNC(sys_game_watchdog_start), //372 (0x174)
|
||||
NULL_FUNC(sys_game_watchdog_stop), //373 (0x175)
|
||||
NULL_FUNC(sys_game_watchdog_clear), //374 (0x176)
|
||||
BIND_SYSC(_sys_game_watchdog_start), //372 (0x174)
|
||||
BIND_SYSC(_sys_game_watchdog_stop), //373 (0x175)
|
||||
BIND_SYSC(_sys_game_watchdog_clear), //374 (0x176)
|
||||
NULL_FUNC(sys_game_set_system_sw_version), //375 (0x177) ROOT
|
||||
NULL_FUNC(sys_game_get_system_sw_version), //376 (0x178) ROOT
|
||||
BIND_SYSC(_sys_game_get_system_sw_version), //376 (0x178) ROOT
|
||||
NULL_FUNC(sys_sm_set_shop_mode), //377 (0x179) ROOT
|
||||
BIND_SYSC(sys_sm_get_ext_event2), //378 (0x17A) ROOT
|
||||
BIND_SYSC(sys_sm_shutdown), //379 (0x17B) ROOT
|
||||
@@ -451,7 +458,7 @@ const std::array<std::pair<ppu_intrp_func_t, std::string_view>, 1024> g_ppu_sysc
|
||||
null_func,//BIND_SYSC(sys_...), //407 (0x197) PM
|
||||
NULL_FUNC(sys_sm_get_tzpb), //408 (0x198) PM
|
||||
NULL_FUNC(sys_sm_get_fan_policy), //409 (0x199) PM
|
||||
NULL_FUNC(sys_game_board_storage_read), //410 (0x19A)
|
||||
BIND_SYSC(_sys_game_board_storage_read), //410 (0x19A)
|
||||
NULL_FUNC(sys_game_board_storage_write), //411 (0x19B)
|
||||
NULL_FUNC(sys_game_get_rtc_status), //412 (0x19C)
|
||||
null_func,//BIND_SYSC(sys_...), //413 (0x19D) ROOT
|
||||
@@ -781,9 +788,9 @@ const std::array<std::pair<ppu_intrp_func_t, std::string_view>, 1024> g_ppu_sysc
|
||||
BIND_SYSC(sys_fs_symbolic_link), //833 (0x341)
|
||||
BIND_SYSC(sys_fs_chmod), //834 (0x342)
|
||||
BIND_SYSC(sys_fs_chown), //835 (0x343)
|
||||
NULL_FUNC(sys_fs_newfs), //836 (0x344)
|
||||
BIND_SYSC(sys_fs_newfs), //836 (0x344)
|
||||
BIND_SYSC(sys_fs_mount), //837 (0x345)
|
||||
NULL_FUNC(sys_fs_unmount), //838 (0x346)
|
||||
BIND_SYSC(sys_fs_unmount), //838 (0x346)
|
||||
NULL_FUNC(sys_fs_sync), //839 (0x347)
|
||||
BIND_SYSC(sys_fs_disk_free), //840 (0x348)
|
||||
BIND_SYSC(sys_fs_get_mount_info_size), //841 (0x349)
|
||||
@@ -1201,6 +1208,11 @@ static std::deque<std::pair<u64, class cpu_thread*>> g_waiting;
|
||||
// Threads which must call lv2_obj::sleep before the scheduler starts
|
||||
static std::deque<class cpu_thread*> g_to_sleep;
|
||||
|
||||
static atomic_t<u64> s_yield_frequency = 0;
|
||||
static atomic_t<u64> s_max_allowed_yield_tsc = 0;
|
||||
static u64 s_last_yield_tsc = 0;
|
||||
atomic_t<u32> g_lv2_preempts_taken = 0;
|
||||
|
||||
namespace cpu_counter
|
||||
{
|
||||
void remove(cpu_thread*) noexcept;
|
||||
@@ -1332,7 +1344,7 @@ bool lv2_obj::sleep_unlocked(cpu_thread& thread, u64 timeout, u64 current_time)
|
||||
val += cpu_flag::suspend;
|
||||
|
||||
// Flag used for forced timeout notification
|
||||
ensure(!timeout || !(val & cpu_flag::notify));
|
||||
ensure(!timeout || !(val & cpu_flag::notify));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1576,6 +1588,7 @@ void lv2_obj::cleanup()
|
||||
g_to_sleep.clear();
|
||||
g_waiting.clear();
|
||||
g_pending = 0;
|
||||
s_yield_frequency = 0;
|
||||
}
|
||||
|
||||
void lv2_obj::schedule_all(u64 current_time)
|
||||
@@ -1592,9 +1605,13 @@ void lv2_obj::schedule_all(u64 current_time)
|
||||
if (target->state & cpu_flag::suspend)
|
||||
{
|
||||
ppu_log.trace("schedule(): %s", target->id);
|
||||
target->state.atomic_op(FN(x += cpu_flag::signal, x -= cpu_flag::suspend));
|
||||
target->start_time = 0;
|
||||
|
||||
if ((target->state.fetch_op(FN(x += cpu_flag::signal, x -= cpu_flag::suspend, void())) & (cpu_flag::wait + cpu_flag::signal)) != cpu_flag::wait)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (notify_later_idx == std::size(g_to_notify))
|
||||
{
|
||||
// Out of notification slots, notify locally (resizable container is not worth it)
|
||||
@@ -1647,11 +1664,56 @@ void lv2_obj::schedule_all(u64 current_time)
|
||||
}
|
||||
}
|
||||
|
||||
if (notify_later_idx - 1 < std::size(g_to_notify) - 1)
|
||||
if (notify_later_idx - 1 < std::size(g_to_notify) - 1)
|
||||
{
|
||||
// Null-terminate the list if it ends before last slot
|
||||
g_to_notify[notify_later_idx] = nullptr;
|
||||
}
|
||||
|
||||
if (const u64 freq = s_yield_frequency)
|
||||
{
|
||||
const u64 tsc = utils::get_tsc();
|
||||
const u64 last_tsc = s_last_yield_tsc;
|
||||
|
||||
if (tsc >= last_tsc && tsc <= s_max_allowed_yield_tsc && tsc - last_tsc >= freq)
|
||||
{
|
||||
auto target = +g_ppu;
|
||||
cpu_thread* cpu = nullptr;
|
||||
|
||||
for (usz x = g_cfg.core.ppu_threads;; target = target->next_ppu, x--)
|
||||
{
|
||||
if (!target || !x)
|
||||
{
|
||||
if (g_ppu && cpu_flag::preempt - g_ppu->state)
|
||||
{
|
||||
// Don't be picky, pick up any running PPU thread even it has a wait flag
|
||||
cpu = g_ppu;
|
||||
}
|
||||
// TODO: If this case is common enough it may be valuable to iterate over all CPU threads to find a perfect candidate (one without a wait or suspend flag)
|
||||
else if (auto current = cpu_thread::get_current(); current && cpu_flag::suspend - current->state)
|
||||
{
|
||||
// May be an SPU or RSX thread, use them as a last resort
|
||||
cpu = current;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (target->state.none_of(cpu_flag::preempt + cpu_flag::wait))
|
||||
{
|
||||
cpu = target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu && cpu_flag::preempt - cpu->state && !cpu->state.test_and_set(cpu_flag::preempt))
|
||||
{
|
||||
s_last_yield_tsc = tsc;
|
||||
g_lv2_preempts_taken.release(g_lv2_preempts_taken.load() + 1); // Has a minor race but performance is more important
|
||||
rsx::set_rsx_yield_flag();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ppu_thread_status lv2_obj::ppu_state(ppu_thread* ppu, bool lock_idm, bool lock_lv2)
|
||||
@@ -1736,6 +1798,13 @@ bool lv2_obj::has_ppus_in_running_state()
|
||||
return false;
|
||||
}
|
||||
|
||||
void lv2_obj::set_yield_frequency(u64 freq, u64 max_allowed_tsc)
|
||||
{
|
||||
s_yield_frequency.release(freq);
|
||||
s_max_allowed_yield_tsc.release(max_allowed_tsc);
|
||||
g_lv2_preempts_taken.release(0);
|
||||
}
|
||||
|
||||
bool lv2_obj::wait_timeout(u64 usec, ppu_thread* cpu, bool scale, bool is_usleep)
|
||||
{
|
||||
static_assert(u64{umax} / max_timeout >= 100, "max timeout is not valid for scaling");
|
||||
@@ -1841,3 +1910,9 @@ bool lv2_obj::wait_timeout(u64 usec, ppu_thread* cpu, bool scale, bool is_usleep
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void lv2_obj::prepare_for_sleep(cpu_thread& cpu)
|
||||
{
|
||||
vm::temporary_unlock(cpu);
|
||||
cpu_counter::remove(&cpu);
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ error_code sys_cond_create(ppu_thread& ppu, vm::ptr<u32> cond_id, u32 mutex_id,
|
||||
return error;
|
||||
}
|
||||
|
||||
ppu.check_state();
|
||||
*cond_id = idm::last_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -451,7 +452,7 @@ error_code sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout)
|
||||
}
|
||||
else
|
||||
{
|
||||
thread_ctrl::wait_on(ppu.state, state);
|
||||
ppu.state.wait(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -220,6 +220,7 @@ error_code sys_event_queue_create(cpu_thread& cpu, vm::ptr<u32> equeue_id, vm::p
|
||||
return error;
|
||||
}
|
||||
|
||||
cpu.check_state();
|
||||
*equeue_id = idm::last_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -381,7 +382,9 @@ error_code sys_event_queue_tryreceive(ppu_thread& ppu, u32 equeue_id, vm::ptr<sy
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
std::lock_guard lock(queue->mutex);
|
||||
std::array<sys_event_t, 127> events;
|
||||
|
||||
std::unique_lock lock(queue->mutex);
|
||||
|
||||
if (!queue->exists)
|
||||
{
|
||||
@@ -392,13 +395,17 @@ error_code sys_event_queue_tryreceive(ppu_thread& ppu, u32 equeue_id, vm::ptr<sy
|
||||
|
||||
while (count < size && !queue->events.empty())
|
||||
{
|
||||
auto& dest = event_array[count++];
|
||||
auto& dest = events[count++];
|
||||
const auto event = queue->events.front();
|
||||
queue->events.pop_front();
|
||||
|
||||
std::tie(dest.source, dest.data1, dest.data2, dest.data3) = event;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
ppu.check_state();
|
||||
|
||||
std::copy_n(events.begin(), count, event_array.get_ptr());
|
||||
*number = count;
|
||||
|
||||
return CELL_OK;
|
||||
@@ -517,7 +524,7 @@ error_code sys_event_queue_receive(ppu_thread& ppu, u32 equeue_id, vm::ptr<sys_e
|
||||
}
|
||||
else
|
||||
{
|
||||
thread_ctrl::wait_on(ppu.state, state);
|
||||
ppu.state.wait(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,6 +566,7 @@ error_code sys_event_port_create(cpu_thread& cpu, vm::ptr<u32> eport_id, s32 por
|
||||
|
||||
if (const u32 id = idm::make<lv2_obj, lv2_event_port>(port_type, name))
|
||||
{
|
||||
cpu.check_state();
|
||||
*eport_id = id;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ error_code sys_event_flag_create(ppu_thread& ppu, vm::ptr<u32> id, vm::ptr<sys_e
|
||||
return error;
|
||||
}
|
||||
|
||||
ppu.check_state();
|
||||
*id = idm::last_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -131,7 +132,11 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm
|
||||
|
||||
~store_result() noexcept
|
||||
{
|
||||
if (ptr) *ptr = val;
|
||||
if (ptr)
|
||||
{
|
||||
cpu_thread::get_current()->check_state();
|
||||
*ptr = val;
|
||||
}
|
||||
}
|
||||
} store{result};
|
||||
|
||||
@@ -249,7 +254,7 @@ error_code sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm
|
||||
}
|
||||
else
|
||||
{
|
||||
thread_ctrl::wait_on(ppu.state, state);
|
||||
ppu.state.wait(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +268,21 @@ 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);
|
||||
|
||||
if (result) *result = 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};
|
||||
|
||||
if (!lv2_event_flag::check_mode(mode))
|
||||
{
|
||||
@@ -271,7 +290,7 @@ error_code sys_event_flag_trywait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode,
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
u64 pattern;
|
||||
u64 pattern{};
|
||||
|
||||
const auto flag = idm::check<lv2_obj, lv2_event_flag>(id, [&](lv2_event_flag& flag)
|
||||
{
|
||||
@@ -291,7 +310,7 @@ error_code sys_event_flag_trywait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode,
|
||||
return not_an_error(CELL_EBUSY);
|
||||
}
|
||||
|
||||
if (result) *result = pattern;
|
||||
store.val = pattern;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -503,6 +522,8 @@ 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;
|
||||
|
||||
+265
-58
@@ -10,6 +10,8 @@
|
||||
#include "Emu/VFS.h"
|
||||
#include "Emu/vfs_config.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/system_utils.hpp"
|
||||
#include "Emu/Cell/lv2/sys_process.h"
|
||||
#include "Emu/RSX/Overlays/overlay_utils.h" // for ascii8_to_utf16
|
||||
#include "Utilities/StrUtil.h"
|
||||
|
||||
@@ -20,15 +22,15 @@ LOG_CHANNEL(sys_fs);
|
||||
|
||||
lv2_fs_mount_point g_mp_sys_dev_root;
|
||||
lv2_fs_mount_point g_mp_sys_no_device;
|
||||
lv2_fs_mount_point g_mp_sys_dev_hdd0{"/dev_hdd0"};
|
||||
lv2_fs_mount_point g_mp_sys_dev_hdd1{"/dev_hdd1", 512, 32768, lv2_mp_flag::no_uid_gid + lv2_mp_flag::cache};
|
||||
lv2_fs_mount_point g_mp_sys_dev_usb{"", 512, 4096, lv2_mp_flag::no_uid_gid};
|
||||
lv2_fs_mount_point g_mp_sys_dev_bdvd{"", 2048, 65536, lv2_mp_flag::read_only + lv2_mp_flag::no_uid_gid};
|
||||
lv2_fs_mount_point g_mp_sys_dev_dvd{"", 2048, 32768, lv2_mp_flag::read_only + lv2_mp_flag::no_uid_gid};
|
||||
lv2_fs_mount_point g_mp_sys_host_root{"", 512, 512, lv2_mp_flag::strict_get_block_size + lv2_mp_flag::no_uid_gid};
|
||||
lv2_fs_mount_point g_mp_sys_dev_flash{"", 512, 8192, lv2_mp_flag::read_only + lv2_mp_flag::no_uid_gid};
|
||||
lv2_fs_mount_point g_mp_sys_dev_flash2{ "", 512, 8192, lv2_mp_flag::no_uid_gid }; // TODO confirm
|
||||
lv2_fs_mount_point g_mp_sys_dev_flash3{ "", 512, 8192, lv2_mp_flag::read_only + lv2_mp_flag::no_uid_gid }; // TODO confirm
|
||||
lv2_fs_mount_point g_mp_sys_dev_hdd0{"/dev_hdd0", 512, 0x24FAEA98};
|
||||
lv2_fs_mount_point g_mp_sys_dev_hdd1{"/dev_hdd1", 512, 0x3FFFF8, 32768, lv2_mp_flag::no_uid_gid + lv2_mp_flag::cache};
|
||||
lv2_fs_mount_point g_mp_sys_dev_usb{"", 512, 0x100, 4096, lv2_mp_flag::no_uid_gid};
|
||||
lv2_fs_mount_point g_mp_sys_dev_bdvd{"", 2048, 0x4D955, 65536, lv2_mp_flag::read_only + lv2_mp_flag::no_uid_gid};
|
||||
lv2_fs_mount_point g_mp_sys_dev_dvd{"", 2048, 0x100, 32768, lv2_mp_flag::read_only + lv2_mp_flag::no_uid_gid};
|
||||
lv2_fs_mount_point g_mp_sys_host_root{"", 512, 0x100, 512, lv2_mp_flag::strict_get_block_size + lv2_mp_flag::no_uid_gid};
|
||||
lv2_fs_mount_point g_mp_sys_dev_flash{"", 512, 0x63E00, 8192, lv2_mp_flag::read_only + lv2_mp_flag::no_uid_gid};
|
||||
lv2_fs_mount_point g_mp_sys_dev_flash2{"", 512, 0x8000, 8192, lv2_mp_flag::no_uid_gid}; // TODO confirm
|
||||
lv2_fs_mount_point g_mp_sys_dev_flash3{"", 512, 0x400, 8192, lv2_mp_flag::read_only + lv2_mp_flag::no_uid_gid}; // TODO confirm
|
||||
|
||||
template<>
|
||||
void fmt_class_string<lv2_file_type>::format(std::string& out, u64 arg)
|
||||
@@ -119,7 +121,7 @@ bool verify_mself(const fs::file& mself_file)
|
||||
return true;
|
||||
}
|
||||
|
||||
lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename)
|
||||
std::string_view lv2_fs_object::get_device_path(std::string_view filename)
|
||||
{
|
||||
std::string_view mp_name, vpath = filename;
|
||||
|
||||
@@ -131,7 +133,7 @@ lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename)
|
||||
if (pos == 0)
|
||||
{
|
||||
// Relative path (TODO)
|
||||
return &g_mp_sys_no_device;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (pos == umax)
|
||||
@@ -169,6 +171,13 @@ lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename)
|
||||
}
|
||||
}
|
||||
|
||||
return mp_name;
|
||||
}
|
||||
|
||||
lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename)
|
||||
{
|
||||
const auto mp_name = get_device_path(filename);
|
||||
|
||||
if (!mp_name.empty())
|
||||
{
|
||||
if (mp_name == "dev_hdd0"sv)
|
||||
@@ -200,6 +209,41 @@ lv2_fs_mount_point* lv2_fs_object::get_mp(std::string_view filename)
|
||||
return &g_mp_sys_dev_root;
|
||||
}
|
||||
|
||||
std::string lv2_fs_object::get_vfs(std::string_view filename)
|
||||
{
|
||||
const auto mp_name = get_device_path(filename);
|
||||
|
||||
if (!mp_name.empty())
|
||||
{
|
||||
if (mp_name == "dev_hdd0"sv)
|
||||
return g_cfg_vfs.get(g_cfg_vfs.dev_hdd0, rpcs3::utils::get_emu_dir());
|
||||
if (mp_name == "dev_hdd1"sv)
|
||||
return g_cfg_vfs.get(g_cfg_vfs.dev_hdd1, rpcs3::utils::get_emu_dir());
|
||||
if (mp_name.starts_with("dev_usb"sv))
|
||||
return g_cfg_vfs.get_device(g_cfg_vfs.dev_usb, fmt::format("/%s", mp_name), rpcs3::utils::get_emu_dir()).path;
|
||||
if (mp_name == "dev_bdvd"sv)
|
||||
return g_cfg_vfs.get(g_cfg_vfs.dev_bdvd, rpcs3::utils::get_emu_dir());
|
||||
if (mp_name == "dev_ps2disc"sv)
|
||||
return {}; // Unsupported in VFS
|
||||
if (mp_name == "app_home"sv && filename.data() != Emu.argv[0].data())
|
||||
return lv2_fs_object::get_vfs(Emu.argv[0]);
|
||||
if (mp_name == "host_root"sv)
|
||||
return g_cfg.vfs.host_root ? "/" : std::string();
|
||||
if (mp_name == "dev_flash"sv)
|
||||
return g_cfg_vfs.get_dev_flash();
|
||||
if (mp_name == "dev_flash2"sv)
|
||||
return g_cfg_vfs.get_dev_flash2();
|
||||
if (mp_name == "dev_flash3"sv)
|
||||
return g_cfg_vfs.get_dev_flash3();
|
||||
|
||||
// Default
|
||||
return g_cfg_vfs.get(g_cfg_vfs.dev_hdd0, rpcs3::utils::get_emu_dir());
|
||||
}
|
||||
|
||||
// Default fallback
|
||||
return {};
|
||||
}
|
||||
|
||||
lv2_fs_object::lv2_fs_object(utils::serial& ar, bool)
|
||||
: name(ar)
|
||||
, mp(get_mp(name.data()))
|
||||
@@ -480,7 +524,7 @@ fs::file lv2_file::make_view(const std::shared_ptr<lv2_file>& _file, u64 offset)
|
||||
return result;
|
||||
}
|
||||
|
||||
std::pair<CellError, std::string_view> translate_to_sv(vm::cptr<char> ptr)
|
||||
std::pair<CellError, std::string_view> translate_to_sv(vm::cptr<char> ptr, bool is_path = true)
|
||||
{
|
||||
const u32 addr = ptr.addr();
|
||||
|
||||
@@ -509,7 +553,7 @@ std::pair<CellError, std::string_view> translate_to_sv(vm::cptr<char> ptr)
|
||||
return {CELL_EFAULT, path};
|
||||
}
|
||||
|
||||
if (!path.starts_with("/"sv))
|
||||
if (is_path && !path.starts_with("/"sv))
|
||||
{
|
||||
return {CELL_ENOENT, path};
|
||||
}
|
||||
@@ -785,7 +829,7 @@ lv2_file::open_result_t lv2_file::open(std::string_view vpath, s32 flags, s32 mo
|
||||
if (size == 8)
|
||||
{
|
||||
// see lv2_file::open_raw
|
||||
switch (*static_cast<const be_t<u64>*>(arg))
|
||||
switch (*static_cast<const be_t<u64, 1>*>(arg))
|
||||
{
|
||||
case 0x18000000010: type = lv2_file_type::sdata; break;
|
||||
case 0x2: type = lv2_file_type::edata; break;
|
||||
@@ -839,6 +883,7 @@ error_code sys_fs_open(ppu_thread& ppu, vm::cptr<char> path, s32 flags, vm::ptr<
|
||||
return result;
|
||||
}))
|
||||
{
|
||||
ppu.check_state();
|
||||
*fd = id;
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -882,11 +927,12 @@ error_code sys_fs_read(ppu_thread& ppu, u32 fd, vm::ptr<void> buf, u64 nbytes, v
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
ppu.check_state();
|
||||
*nread = 0;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
std::lock_guard lock(file->mp->mutex);
|
||||
std::unique_lock lock(file->mp->mutex);
|
||||
|
||||
if (!file->file)
|
||||
{
|
||||
@@ -900,10 +946,13 @@ error_code sys_fs_read(ppu_thread& ppu, u32 fd, vm::ptr<void> buf, u64 nbytes, v
|
||||
}
|
||||
|
||||
const u64 read_bytes = file->op_read(buf, nbytes);
|
||||
const bool failure = !read_bytes && file->file.pos() < file->file.size();
|
||||
lock.unlock();
|
||||
ppu.check_state();
|
||||
|
||||
*nread = read_bytes;
|
||||
|
||||
if (!read_bytes && file->file.pos() < file->file.size())
|
||||
if (failure)
|
||||
{
|
||||
// EDATA corruption perhaps
|
||||
return CELL_EFSSPECIFIC;
|
||||
@@ -947,6 +996,7 @@ error_code sys_fs_write(ppu_thread& ppu, u32 fd, vm::cptr<void> buf, u64 nbytes,
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
ppu.check_state();
|
||||
*nwrite = 0;
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -962,7 +1012,7 @@ error_code sys_fs_write(ppu_thread& ppu, u32 fd, vm::cptr<void> buf, u64 nbytes,
|
||||
return CELL_EROFS;
|
||||
}
|
||||
|
||||
std::lock_guard lock(file->mp->mutex);
|
||||
std::unique_lock lock(file->mp->mutex);
|
||||
|
||||
if (!file->file)
|
||||
{
|
||||
@@ -986,8 +1036,11 @@ error_code sys_fs_write(ppu_thread& ppu, u32 fd, vm::cptr<void> buf, u64 nbytes,
|
||||
file->file.seek(0, fs::seek_end);
|
||||
}
|
||||
|
||||
*nwrite = file->op_write(buf, nbytes);
|
||||
const u64 written = file->op_write(buf, nbytes);
|
||||
lock.unlock();
|
||||
ppu.check_state();
|
||||
|
||||
*nwrite = written;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1102,7 +1155,7 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u32> fd)
|
||||
return {CELL_ENOTDIR, path};
|
||||
}
|
||||
|
||||
std::lock_guard lock(mp->mutex);
|
||||
std::unique_lock lock(mp->mutex);
|
||||
|
||||
const fs::dir dir(local_path);
|
||||
|
||||
@@ -1156,7 +1209,7 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u32> fd)
|
||||
}
|
||||
else
|
||||
{
|
||||
data.emplace_back().name = ".";
|
||||
data.emplace_back().name += '.';
|
||||
data.back().is_directory = true;
|
||||
data.emplace_back().name = "..";
|
||||
data.back().is_directory = true;
|
||||
@@ -1177,6 +1230,9 @@ error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u32> fd)
|
||||
|
||||
if (const u32 id = idm::make<lv2_fs_object, lv2_dir>(processed_path, std::move(data)))
|
||||
{
|
||||
lock.unlock();
|
||||
ppu.check_state();
|
||||
|
||||
*fd = id;
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1199,6 +1255,8 @@ error_code sys_fs_readdir(ppu_thread& ppu, u32 fd, vm::ptr<CellFsDirent> dir, vm
|
||||
return CELL_EBADF;
|
||||
}
|
||||
|
||||
ppu.check_state();
|
||||
|
||||
if (auto* info = directory->dir_read())
|
||||
{
|
||||
dir->d_type = info->is_directory ? CELL_FS_TYPE_DIRECTORY : CELL_FS_TYPE_REGULAR;
|
||||
@@ -1265,7 +1323,7 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
|
||||
return {CELL_ENOTMOUNTED, path};
|
||||
}
|
||||
|
||||
std::lock_guard lock(mp->mutex);
|
||||
std::unique_lock lock(mp->mutex);
|
||||
|
||||
fs::stat_t info{};
|
||||
|
||||
@@ -1308,7 +1366,18 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
|
||||
}
|
||||
}
|
||||
|
||||
sb->mode = info.is_directory ? CELL_FS_S_IFDIR | 0777 : CELL_FS_S_IFREG | 0666;
|
||||
lock.unlock();
|
||||
ppu.check_state();
|
||||
|
||||
s32 mode = info.is_directory ? CELL_FS_S_IFDIR | 0777 : CELL_FS_S_IFREG | 0666;
|
||||
|
||||
if (mp->flags & lv2_mp_flag::read_only)
|
||||
{
|
||||
// Remove write permissions
|
||||
mode &= ~0222;
|
||||
}
|
||||
|
||||
sb->mode = mode;
|
||||
sb->uid = mp->flags & lv2_mp_flag::no_uid_gid ? -1 : 0;
|
||||
sb->gid = mp->flags & lv2_mp_flag::no_uid_gid ? -1 : 0;
|
||||
sb->atime = info.atime;
|
||||
@@ -1317,12 +1386,6 @@ error_code sys_fs_stat(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<CellFsStat>
|
||||
sb->size = info.is_directory ? mp->block_size : info.size;
|
||||
sb->blksize = mp->block_size;
|
||||
|
||||
if (mp->flags & lv2_mp_flag::read_only)
|
||||
{
|
||||
// Remove write permissions
|
||||
sb->mode &= ~0222;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -1340,7 +1403,7 @@ error_code sys_fs_fstat(ppu_thread& ppu, u32 fd, vm::ptr<CellFsStat> sb)
|
||||
return CELL_EBADF;
|
||||
}
|
||||
|
||||
std::lock_guard lock(file->mp->mutex);
|
||||
std::unique_lock lock(file->mp->mutex);
|
||||
|
||||
if (!file->file)
|
||||
{
|
||||
@@ -1352,9 +1415,19 @@ error_code sys_fs_fstat(ppu_thread& ppu, u32 fd, vm::ptr<CellFsStat> sb)
|
||||
return CELL_EIO;
|
||||
}
|
||||
|
||||
const fs::stat_t& info = file->file.stat();
|
||||
const fs::stat_t info = file->file.stat();
|
||||
lock.unlock();
|
||||
ppu.check_state();
|
||||
|
||||
sb->mode = info.is_directory ? CELL_FS_S_IFDIR | 0777 : CELL_FS_S_IFREG | 0666;
|
||||
s32 mode = info.is_directory ? CELL_FS_S_IFDIR | 0777 : CELL_FS_S_IFREG | 0666;
|
||||
|
||||
if (file->mp->flags & lv2_mp_flag::read_only)
|
||||
{
|
||||
// Remove write permissions
|
||||
mode &= ~0222;
|
||||
}
|
||||
|
||||
sb->mode = mode;
|
||||
sb->uid = file->mp->flags & lv2_mp_flag::no_uid_gid ? -1 : 0;
|
||||
sb->gid = file->mp->flags & lv2_mp_flag::no_uid_gid ? -1 : 0;
|
||||
sb->atime = info.atime;
|
||||
@@ -1362,13 +1435,6 @@ error_code sys_fs_fstat(ppu_thread& ppu, u32 fd, vm::ptr<CellFsStat> sb)
|
||||
sb->ctime = info.ctime; // ctime may be incorrect
|
||||
sb->size = info.size;
|
||||
sb->blksize = file->mp->block_size;
|
||||
|
||||
if (file->mp->flags & lv2_mp_flag::read_only)
|
||||
{
|
||||
// Remove write permissions
|
||||
sb->mode &= ~0222;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -2180,18 +2246,28 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
|
||||
return CELL_EBADF;
|
||||
}
|
||||
|
||||
arg->_size = 0; // This write is not really useful for cellFs but do it anyways
|
||||
ppu.check_state();
|
||||
|
||||
u32 read_count = 0;
|
||||
|
||||
// NOTE: This function is actually capable of reading only one entry at a time
|
||||
if (arg->max)
|
||||
if (const u32 max = arg->max)
|
||||
{
|
||||
std::memset(arg->ptr.get_ptr(), 0, arg->max * arg->ptr.size());
|
||||
const auto arg_ptr = +arg->ptr;
|
||||
|
||||
if (auto* info = directory->dir_read())
|
||||
{
|
||||
auto& entry = arg->ptr[arg->_size++];
|
||||
auto& entry = arg_ptr[read_count++];
|
||||
|
||||
entry.attribute.mode = info->is_directory ? CELL_FS_S_IFDIR | 0777 : CELL_FS_S_IFREG | 0666;
|
||||
s32 mode = info->is_directory ? CELL_FS_S_IFDIR | 0777 : CELL_FS_S_IFREG | 0666;
|
||||
|
||||
if (directory->mp->flags & lv2_mp_flag::read_only)
|
||||
{
|
||||
// Remove write permissions
|
||||
mode &= ~0222;
|
||||
}
|
||||
|
||||
entry.attribute.mode = mode;
|
||||
entry.attribute.uid = directory->mp->flags & lv2_mp_flag::no_uid_gid ? -1 : 0;
|
||||
entry.attribute.gid = directory->mp->flags & lv2_mp_flag::no_uid_gid ? -1 : 0;
|
||||
entry.attribute.atime = info->atime;
|
||||
@@ -2200,18 +2276,16 @@ error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr<void> _arg, u32
|
||||
entry.attribute.size = info->size;
|
||||
entry.attribute.blksize = directory->mp->block_size;
|
||||
|
||||
if (directory->mp->flags & lv2_mp_flag::read_only)
|
||||
{
|
||||
// Remove write permissions
|
||||
entry.attribute.mode &= ~0222;
|
||||
}
|
||||
|
||||
entry.entry_name.d_type = info->is_directory ? CELL_FS_TYPE_DIRECTORY : CELL_FS_TYPE_REGULAR;
|
||||
entry.entry_name.d_namlen = u8(std::min<usz>(info->name.size(), CELL_FS_MAX_FS_FILE_NAME_LENGTH));
|
||||
strcpy_trunc(entry.entry_name.d_name, info->name);
|
||||
}
|
||||
|
||||
// Apparently all this function does to additional buffer elements is to zeroize them
|
||||
std::memset(arg_ptr.get_ptr() + read_count, 0, (max - read_count) * arg->ptr.size());
|
||||
}
|
||||
|
||||
arg->_size = read_count;
|
||||
arg->_code = CELL_OK;
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -2342,7 +2416,7 @@ error_code sys_fs_lseek(ppu_thread& ppu, u32 fd, s64 offset, s32 whence, vm::ptr
|
||||
return CELL_EBADF;
|
||||
}
|
||||
|
||||
std::lock_guard lock(file->mp->mutex);
|
||||
std::unique_lock lock(file->mp->mutex);
|
||||
|
||||
if (!file->file)
|
||||
{
|
||||
@@ -2367,6 +2441,9 @@ error_code sys_fs_lseek(ppu_thread& ppu, u32 fd, s64 offset, s32 whence, vm::ptr
|
||||
return CELL_EIO; // ???
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
ppu.check_state();
|
||||
|
||||
*pos = result;
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -2665,24 +2742,31 @@ error_code sys_fs_disk_free(ppu_thread& ppu, vm::cptr<char> path, vm::ptr<u64> t
|
||||
if (mp->flags & lv2_mp_flag::read_only)
|
||||
{
|
||||
// TODO: check /dev_bdvd
|
||||
ppu.check_state();
|
||||
*total_free = 0;
|
||||
*avail_free = 0;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
u64 available = 0;
|
||||
|
||||
// avail_free is the only value used by cellFsGetFreeSize
|
||||
if (mp == &g_mp_sys_dev_hdd1)
|
||||
{
|
||||
*avail_free = (1u << 31) - mp->sector_size; // 2GB (TODO: Should be the total size)
|
||||
available = (1u << 31) - mp->sector_size; // 2GB (TODO: Should be the total size)
|
||||
}
|
||||
else //if (mp == &g_mp_sys_dev_hdd0)
|
||||
{
|
||||
*avail_free = (40ull * 1024 * 1024 * 1024 - mp->sector_size); // Read explanation in cellHddGameCheck
|
||||
available = (40ull * 1024 * 1024 * 1024 - mp->sector_size); // Read explanation in cellHddGameCheck
|
||||
}
|
||||
|
||||
// HACK: Hopefully nothing uses this value or once at max because its hacked here:
|
||||
// The total size can change based on the size of the directory
|
||||
*total_free = *avail_free + fs::get_dir_size(local_path, mp->sector_size);
|
||||
const u64 total = available + fs::get_dir_size(local_path, mp->sector_size);
|
||||
|
||||
ppu.check_state();
|
||||
*total_free = total;
|
||||
*avail_free = available;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -2855,7 +2939,7 @@ error_code sys_fs_get_mount_info_size(ppu_thread&, vm::ptr<u64> len)
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
*len = 0x8;
|
||||
*len = 0x9;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -2875,7 +2959,7 @@ error_code sys_fs_get_mount_info(ppu_thread&, vm::ptr<CellFsMountInfo> info, u32
|
||||
sys_fs.todo("sys_fs_get_mount_info special case TODO");
|
||||
}
|
||||
|
||||
const u32 max_len = std::min<u32>(len, 8);
|
||||
const u32 max_len = std::min<u32>(len, 9);
|
||||
*out_len = max_len;
|
||||
|
||||
struct mount_info
|
||||
@@ -2884,7 +2968,7 @@ error_code sys_fs_get_mount_info(ppu_thread&, vm::ptr<CellFsMountInfo> info, u32
|
||||
be_t<u32> unk1 = 0, unk2 = 0, unk3 = 0, unk4 = 0, unk5 = 0;
|
||||
};
|
||||
|
||||
static constexpr std::array<mount_info, 8> data
|
||||
static constexpr std::array<mount_info, 9> data
|
||||
{
|
||||
mount_info{.path = "/", .filesystem = "CELL_FS_ADMINFS", .dev_name = "CELL_FS_ADMINFS:", .unk5 = 0x10000000},
|
||||
mount_info{.path = "/app_home", .filesystem = "CELL_FS_DUMMY", .dev_name = "CELL_FS_DUMMY:"},
|
||||
@@ -2893,6 +2977,7 @@ error_code sys_fs_get_mount_info(ppu_thread&, vm::ptr<CellFsMountInfo> info, u32
|
||||
mount_info{.path = "/dev_flash2", .filesystem = "CELL_FS_FAT", .dev_name = "CELL_FS_IOS:BUILTIN_FLSH2:"},
|
||||
mount_info{.path = "/dev_flash3", .filesystem = "CELL_FS_FAT", .dev_name = "CELL_FS_IOS:BUILTIN_FLSH3:"},
|
||||
mount_info{.path = "/dev_hdd0", .filesystem = "CELL_FS_UFS", .dev_name = "CELL_FS_UTILITY:HDD0:"},
|
||||
mount_info{.path = "/dev_hdd1", .filesystem = "CELL_FS_FAT", .dev_name = "CELL_FS_UTILITY:HDD1:"},
|
||||
mount_info{.path = "/dev_bdvd", .filesystem = "CELL_FS_ISO9660", .dev_name = "CELL_FS_IOS:PATA0_BDVD_DRIVE"},
|
||||
};
|
||||
|
||||
@@ -2907,9 +2992,131 @@ error_code sys_fs_get_mount_info(ppu_thread&, vm::ptr<CellFsMountInfo> info, u32
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_fs_mount(ppu_thread&, vm::cptr<char> dev_name, vm::cptr<char> file_system, vm::cptr<char> path, s32 unk1, s32 prot, s32 unk3, vm::cptr<char> str1, u32 str_len)
|
||||
error_code sys_fs_newfs(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char> file_system, s32 unk1, vm::cptr<char> str1)
|
||||
{
|
||||
sys_fs.todo("sys_fs_mount(dev_name=%s, file_system=%s, path=%s, unk1=0x%x, prot=0x%x, unk3=0x%x, str1=%s, str_len=%d)", dev_name, file_system, path, unk1, prot, unk3, str1, str_len);
|
||||
sys_fs.todo("sys_fs_newfs(dev_name=%s, file_system=%s, unk1=0x%x, str1=%s)", dev_name, file_system, unk1, str1);
|
||||
|
||||
if (!g_ps3_process_info.has_root_perm())
|
||||
{
|
||||
return CELL_ENOSYS;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_fs_mount(ppu_thread&, vm::cptr<char> dev_name, vm::cptr<char> file_system, vm::cptr<char> path, s32 unk1, s32 prot, s32 unk3, vm::cptr<char> str1, u32 str_len)
|
||||
{
|
||||
sys_fs.warning("sys_fs_mount(dev_name=%s, file_system=%s, path=%s, unk1=0x%x, prot=0x%x, unk3=0x%x, str1=%s, str_len=%d)", dev_name, file_system, path, unk1, prot, unk3, str1, str_len);
|
||||
|
||||
if (!g_ps3_process_info.has_root_perm())
|
||||
{
|
||||
return CELL_ENOSYS;
|
||||
}
|
||||
|
||||
const auto [path_error, vpath] = translate_to_sv(path);
|
||||
|
||||
if (path_error)
|
||||
{
|
||||
return { path_error, vpath };
|
||||
}
|
||||
|
||||
const auto [fs_error, filesystem] = translate_to_sv(file_system, false);
|
||||
|
||||
if (fs_error)
|
||||
{
|
||||
return { fs_error, filesystem };
|
||||
}
|
||||
|
||||
const auto mp = lv2_fs_object::get_mp(vpath);
|
||||
bool success = true;
|
||||
|
||||
auto vfs_mount = [&vpath = vpath, &filesystem = filesystem, &mp = mp](std::string mount_path)
|
||||
{
|
||||
if (!mount_path.ends_with('/'))
|
||||
mount_path += '/';
|
||||
if (!fs::is_dir(mount_path) && !fs::create_dir(mount_path))
|
||||
{
|
||||
sys_fs.error("Failed to create directory \"%s\"", mount_path);
|
||||
return false;
|
||||
}
|
||||
const bool is_simplefs = filesystem == "CELL_FS_SIMPLEFS";
|
||||
if (is_simplefs)
|
||||
{
|
||||
mount_path += "loop.tmp";
|
||||
fs::file loop_file;
|
||||
if (loop_file.open(mount_path, fs::create + fs::read + fs::write + fs::trunc + fs::lock))
|
||||
{
|
||||
const u64 file_size = mp->sector_size * mp->sector_count;
|
||||
loop_file.trunc(file_size);
|
||||
sys_fs.notice("Created a loop file of size 0x%x at \"%s\"", file_size, mount_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_fs.error("Failed to create loop file \"%s\"", mount_path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return vfs::mount(vpath, mount_path, !is_simplefs);
|
||||
};
|
||||
|
||||
if (mp == &g_mp_sys_dev_hdd1)
|
||||
{
|
||||
const std::string_view appname = g_ps3_process_info.get_cellos_appname();
|
||||
success = vfs_mount(fmt::format("%s/caches/%s", lv2_fs_object::get_vfs(vpath), appname.substr(0, appname.find_last_of('.'))));
|
||||
}
|
||||
else
|
||||
{
|
||||
//success = vfs_mount(lv2_fs_object::get_vfs(vpath)); // We are not supporting mounting devices other than /dev_hdd1 via this syscall currently
|
||||
}
|
||||
|
||||
if (!success)
|
||||
return CELL_EIO;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_fs_unmount(ppu_thread&, vm::cptr<char> path, s32 unk1, s32 unk2)
|
||||
{
|
||||
sys_fs.warning("sys_fs_unmount(path=%s, unk1=0x%x, unk2=0x%x)", path, unk1, unk2);
|
||||
|
||||
if (!g_ps3_process_info.has_root_perm())
|
||||
{
|
||||
return CELL_ENOSYS;
|
||||
}
|
||||
|
||||
const auto [path_error, vpath] = translate_to_sv(path);
|
||||
|
||||
if (path_error)
|
||||
{
|
||||
return { path_error, vpath };
|
||||
}
|
||||
|
||||
const auto mp = lv2_fs_object::get_mp(vpath);
|
||||
bool success = true;
|
||||
|
||||
auto vfs_unmount = [&vpath = vpath]()
|
||||
{
|
||||
const std::string local_path = vfs::get(vpath);
|
||||
if (fs::is_file(local_path))
|
||||
{
|
||||
if (fs::remove_file(local_path))
|
||||
{
|
||||
sys_fs.notice("Removed loop file \"%s\"", local_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_fs.error("Failed to remove loop file \"%s\"", local_path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return vfs::unmount(vpath);
|
||||
};
|
||||
|
||||
if (mp == &g_mp_sys_dev_hdd1) // We are not supporting unmounting devices other than /dev_hdd1 via this syscall currently
|
||||
success = vfs_unmount();
|
||||
|
||||
if (!success)
|
||||
return CELL_EIO;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ struct lv2_fs_mount_point
|
||||
{
|
||||
const std::string_view root;
|
||||
const u32 sector_size = 512;
|
||||
const u64 sector_count = 256;
|
||||
const u32 block_size = 4096;
|
||||
const bs_t<lv2_mp_flag> flags{};
|
||||
|
||||
@@ -160,9 +161,10 @@ extern lv2_fs_mount_point g_mp_sys_dev_hdd1;
|
||||
|
||||
struct lv2_fs_object
|
||||
{
|
||||
static const u32 id_base = 3;
|
||||
static const u32 id_step = 1;
|
||||
static const u32 id_count = 255 - id_base;
|
||||
static constexpr u32 id_base = 3;
|
||||
static constexpr u32 id_step = 1;
|
||||
static constexpr u32 id_count = 255 - id_base;
|
||||
static constexpr bool id_lowest = true;
|
||||
SAVESTATE_INIT_POS(40);
|
||||
|
||||
// File Name (max 1055)
|
||||
@@ -185,7 +187,9 @@ public:
|
||||
|
||||
lv2_fs_object& operator=(const lv2_fs_object&) = delete;
|
||||
|
||||
static std::string_view get_device_path(std::string_view filename);
|
||||
static lv2_fs_mount_point* get_mp(std::string_view filename);
|
||||
static std::string get_vfs(std::string_view filename);
|
||||
|
||||
static std::array<char, 0x420> get_name(std::string_view filename)
|
||||
{
|
||||
@@ -613,6 +617,8 @@ error_code sys_fs_lsn_write(ppu_thread& ppu, u32 fd, vm::cptr<void>, u64);
|
||||
error_code sys_fs_mapped_allocate(ppu_thread& ppu, u32 fd, u64, vm::pptr<void> out_ptr);
|
||||
error_code sys_fs_mapped_free(ppu_thread& ppu, u32 fd, vm::ptr<void> ptr);
|
||||
error_code sys_fs_truncate2(ppu_thread& ppu, u32 fd, u64 size);
|
||||
error_code sys_fs_newfs(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char> file_system, s32 unk1, vm::cptr<char> str1);
|
||||
error_code sys_fs_mount(ppu_thread& ppu, vm::cptr<char> dev_name, vm::cptr<char> file_system, vm::cptr<char> path, s32 unk1, s32 prot, s32 unk3, vm::cptr<char> str1, u32 str_len);
|
||||
error_code sys_fs_unmount(ppu_thread&, vm::cptr<char> path, s32 unk1, s32 unk2);
|
||||
error_code sys_fs_get_mount_info_size(ppu_thread& ppu, vm::ptr<u64> len);
|
||||
error_code sys_fs_get_mount_info(ppu_thread& ppu, vm::ptr<CellFsMountInfo> info, u32 len, vm::ptr<u64> out_len);
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
#include "stdafx.h"
|
||||
#include "util/sysinfo.hpp"
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/system_config.h"
|
||||
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Utilities/Thread.h"
|
||||
|
||||
#include "sys_game.h"
|
||||
|
||||
LOG_CHANNEL(sys_game);
|
||||
|
||||
struct watchdog_t
|
||||
{
|
||||
struct alignas(8) control_t
|
||||
{
|
||||
bool needs_restart = false;
|
||||
bool active = false;
|
||||
char pad[sizeof(u32) - sizeof(bool) * 2]{};
|
||||
u32 timeout = 0;
|
||||
};
|
||||
|
||||
atomic_t<control_t> control;
|
||||
|
||||
void operator()()
|
||||
{
|
||||
u64 start_time = get_system_time();
|
||||
u64 old_time = start_time;
|
||||
u64 current_time = old_time;
|
||||
|
||||
constexpr u64 sleep_time = 50'000;
|
||||
|
||||
while (thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
if (Emu.GetStatus(false) == system_state::paused)
|
||||
{
|
||||
start_time += current_time - old_time;
|
||||
old_time = current_time;
|
||||
thread_ctrl::wait_for(sleep_time);
|
||||
current_time = get_system_time();
|
||||
continue;
|
||||
}
|
||||
|
||||
old_time = std::exchange(current_time, get_system_time());
|
||||
|
||||
const auto old = control.fetch_op([&](control_t& data)
|
||||
{
|
||||
if (data.needs_restart)
|
||||
{
|
||||
data.needs_restart = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}).first;
|
||||
|
||||
if (old.active && old.needs_restart)
|
||||
{
|
||||
start_time = current_time;
|
||||
old_time = current_time;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (old.active && current_time - start_time >= old.timeout)
|
||||
{
|
||||
sys_game.success("Watchdog timeout! Restarting the game...");
|
||||
|
||||
Emu.CallFromMainThread([]()
|
||||
{
|
||||
Emu.Restart();
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
thread_ctrl::wait_for(sleep_time);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr auto thread_name = "LV2 Watchdog Thread"sv;
|
||||
};
|
||||
|
||||
error_code _sys_game_watchdog_start(u32 timeout)
|
||||
{
|
||||
sys_game.trace("sys_game_watchdog_start(timeout=%d)", timeout);
|
||||
|
||||
// According to disassembly
|
||||
timeout *= 1'000'000;
|
||||
timeout &= -64;
|
||||
|
||||
if (!g_fxo->get<named_thread<watchdog_t>>().control.fetch_op([&](watchdog_t::control_t& data)
|
||||
{
|
||||
if (data.active)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
data.needs_restart = true;
|
||||
data.active = true;
|
||||
data.timeout = timeout;
|
||||
return true;
|
||||
}).second)
|
||||
{
|
||||
return CELL_EABORT;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code _sys_game_watchdog_stop()
|
||||
{
|
||||
sys_game.trace("sys_game_watchdog_stop()");
|
||||
|
||||
g_fxo->get<named_thread<watchdog_t>>().control.fetch_op([](watchdog_t::control_t& data)
|
||||
{
|
||||
if (!data.active)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
data.active = false;
|
||||
return true;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code _sys_game_watchdog_clear()
|
||||
{
|
||||
sys_game.trace("sys_game_watchdog_clear()");
|
||||
|
||||
g_fxo->get<named_thread<watchdog_t>>().control.fetch_op([](watchdog_t::control_t& data)
|
||||
{
|
||||
if (!data.active || data.needs_restart)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
data.needs_restart = true;
|
||||
return true;
|
||||
});
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
u64 _sys_game_get_system_sw_version()
|
||||
{
|
||||
return stof(utils::get_firmware_version()) * 10000;
|
||||
}
|
||||
|
||||
error_code _sys_game_board_storage_read(vm::ptr<u8> buffer1, vm::ptr<u8> buffer2)
|
||||
{
|
||||
sys_game.trace("sys_game_board_storage_read(buffer1=*0x%x, buffer2=*0x%x)", buffer1, buffer2);
|
||||
|
||||
if (!buffer1)
|
||||
{
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
be_t<u64> psid[2] = { +g_cfg.sys.console_psid_high, +g_cfg.sys.console_psid_low };
|
||||
u8* psid_bytes = reinterpret_cast<u8*>(psid);
|
||||
u8 response[16] = { 0x01, 0xFC, 0x43, 0x50, 0xA7, 0x9B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
response[i] ^= psid_bytes[i];
|
||||
}
|
||||
memcpy(buffer1.get_ptr(), response, 16);
|
||||
|
||||
if (!buffer2)
|
||||
{
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
*buffer2 = 0x00;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
error_code _sys_game_watchdog_start(u32 timeout);
|
||||
error_code _sys_game_watchdog_stop();
|
||||
error_code _sys_game_watchdog_clear();
|
||||
u64 _sys_game_get_system_sw_version();
|
||||
error_code _sys_game_board_storage_read(vm::ptr<u8> buffer1, vm::ptr<u8> buffer2);
|
||||
@@ -190,6 +190,7 @@ error_code _sys_interrupt_thread_establish(ppu_thread& ppu, vm::ptr<u32> ih, u32
|
||||
|
||||
if (id)
|
||||
{
|
||||
ppu.check_state();
|
||||
*ih = id;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ error_code _sys_lwcond_create(ppu_thread& ppu, vm::ptr<u32> lwcond_id, u32 lwmut
|
||||
|
||||
if (const u32 id = idm::make<lv2_obj, lv2_lwcond>(name, lwmutex_id, protocol, control))
|
||||
{
|
||||
ppu.check_state();
|
||||
*lwcond_id = id;
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -486,7 +487,7 @@ error_code _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id
|
||||
}
|
||||
else
|
||||
{
|
||||
thread_ctrl::wait_on(ppu.state, state);
|
||||
ppu.state.wait(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ error_code _sys_lwmutex_create(ppu_thread& ppu, vm::ptr<u32> lwmutex_id, u32 pro
|
||||
|
||||
if (const u32 id = idm::make<lv2_obj, lv2_lwmutex>(protocol, control, name))
|
||||
{
|
||||
ppu.check_state();
|
||||
*lwmutex_id = id;
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -246,7 +247,7 @@ error_code _sys_lwmutex_lock(ppu_thread& ppu, u32 lwmutex_id, u64 timeout)
|
||||
}
|
||||
else
|
||||
{
|
||||
thread_ctrl::wait_on(ppu.state, state);
|
||||
ppu.state.wait(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,6 +132,7 @@ error_code sys_memory_allocate(cpu_thread& cpu, u32 size, u64 flags, vm::ptr<u32
|
||||
if (alloc_addr)
|
||||
{
|
||||
vm::lock_sudo(addr, size);
|
||||
cpu.check_state();
|
||||
*alloc_addr = addr;
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -203,6 +204,7 @@ error_code sys_memory_allocate_from_container(cpu_thread& cpu, u32 size, u32 cid
|
||||
if (alloc_addr)
|
||||
{
|
||||
vm::lock_sudo(addr, size);
|
||||
cpu.check_state();
|
||||
*alloc_addr = addr;
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -282,17 +284,22 @@ error_code sys_memory_get_user_memory_size(cpu_thread& cpu, vm::ptr<sys_memory_i
|
||||
// Get "default" memory container
|
||||
auto& dct = g_fxo->get<lv2_memory_container>();
|
||||
|
||||
::reader_lock lock(s_memstats_mtx);
|
||||
|
||||
mem_info->total_user_memory = dct.size;
|
||||
mem_info->available_user_memory = dct.size - dct.used;
|
||||
|
||||
// Scan other memory containers
|
||||
idm::select<lv2_memory_container>([&](u32, lv2_memory_container& ct)
|
||||
sys_memory_info_t out{};
|
||||
{
|
||||
mem_info->total_user_memory -= ct.size;
|
||||
});
|
||||
::reader_lock lock(s_memstats_mtx);
|
||||
|
||||
out.total_user_memory = dct.size;
|
||||
out.available_user_memory = dct.size - dct.used;
|
||||
|
||||
// Scan other memory containers
|
||||
idm::select<lv2_memory_container>([&](u32, lv2_memory_container& ct)
|
||||
{
|
||||
out.total_user_memory -= ct.size;
|
||||
});
|
||||
}
|
||||
|
||||
cpu.check_state();
|
||||
*mem_info = out;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -332,6 +339,7 @@ error_code sys_memory_container_create(cpu_thread& cpu, vm::ptr<u32> cid, u32 si
|
||||
// Create the memory container
|
||||
if (const u32 id = idm::make<lv2_memory_container>(size, true))
|
||||
{
|
||||
cpu.check_state();
|
||||
*cid = id;
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -388,6 +396,7 @@ error_code sys_memory_container_get_size(cpu_thread& cpu, vm::ptr<sys_memory_inf
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
cpu.check_state();
|
||||
mem_info->total_user_memory = ct->size; // Total container memory
|
||||
mem_info->available_user_memory = ct->size - ct->used; // Available container memory
|
||||
|
||||
|
||||
@@ -174,6 +174,7 @@ error_code sys_mmapper_allocate_address(ppu_thread& ppu, u64 size, u64 flags, u6
|
||||
{
|
||||
if (const auto area = vm::find_map(static_cast<u32>(size), static_cast<u32>(alignment), flags & SYS_MEMORY_PAGE_SIZE_MASK))
|
||||
{
|
||||
ppu.check_state();
|
||||
*alloc_addr = area->addr;
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -246,6 +247,7 @@ error_code sys_mmapper_allocate_shared_memory(ppu_thread& ppu, u64 ipc_key, u64
|
||||
return error;
|
||||
}
|
||||
|
||||
ppu.check_state();
|
||||
*mem_id = idm::last_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -301,6 +303,7 @@ error_code sys_mmapper_allocate_shared_memory_from_container(ppu_thread& ppu, u6
|
||||
return error;
|
||||
}
|
||||
|
||||
ppu.check_state();
|
||||
*mem_id = idm::last_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -399,6 +402,7 @@ error_code sys_mmapper_allocate_shared_memory_ext(ppu_thread& ppu, u64 ipc_key,
|
||||
return error;
|
||||
}
|
||||
|
||||
ppu.check_state();
|
||||
*mem_id = idm::last_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -496,6 +500,7 @@ error_code sys_mmapper_allocate_shared_memory_from_container_ext(ppu_thread& ppu
|
||||
return error;
|
||||
}
|
||||
|
||||
ppu.check_state();
|
||||
*mem_id = idm::last_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -718,6 +723,8 @@ error_code sys_mmapper_search_and_map(ppu_thread& ppu, u32 start_addr, u32 mem_i
|
||||
}
|
||||
|
||||
vm::lock_sudo(addr, mem->size);
|
||||
|
||||
ppu.check_state();
|
||||
*alloc_addr = addr;
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -763,6 +770,7 @@ error_code sys_mmapper_unmap_shared_memory(ppu_thread& ppu, u32 addr, vm::ptr<u3
|
||||
}
|
||||
|
||||
// Write out the ID
|
||||
ppu.check_state();
|
||||
*mem_id = mem.ret;
|
||||
|
||||
// Acknowledge
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user