Compare commits
193 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b70a1edbc7 | |||
| 14e5aa57c8 | |||
| cdc49d166e | |||
| 7b65f93953 | |||
| d878a286bc | |||
| ce35554a55 | |||
| 1f17109203 | |||
| 4739eb3601 | |||
| 40a84dd477 | |||
| 309f409554 | |||
| a6f8e4f6b3 | |||
| 596db2c29c | |||
| 1fd33f611c | |||
| 5e3bacbd9b | |||
| 7cdb5f3123 | |||
| a168aa2df3 | |||
| 87fe93ee9a | |||
| 039e295e53 | |||
| 0fd888a42e | |||
| 1e71285de8 | |||
| a2200bd01e | |||
| 9a9455a696 | |||
| 19a698682b | |||
| 3baf79f929 | |||
| 74d47943e9 | |||
| d9c78cf495 | |||
| 8e4a09d9e5 | |||
| 67ac8bf070 | |||
| ef5225b776 | |||
| 48b0583866 | |||
| f528aada24 | |||
| 1f45f05f8d | |||
| b8fcbb68b0 | |||
| 4ab9a2a3a8 | |||
| 85c0219aed | |||
| 10e218e224 | |||
| 69d044cd8d | |||
| 7062efeb3e | |||
| cee53fcecf | |||
| ef8e92cbe3 | |||
| 7c73c3b75c | |||
| 1e826f5ccf | |||
| 96561c16ad | |||
| bd85f23ed9 | |||
| 557e1c3694 | |||
| c785b14c18 | |||
| 3751d5d5c1 | |||
| 45bb51ded8 | |||
| c3b424eb11 | |||
| 01ef9ecca4 | |||
| 0eb6bf6a67 | |||
| 4aab8db420 | |||
| 9263999ad1 | |||
| 4d9b56c7f1 | |||
| d0afef47f8 | |||
| af53ce6f76 | |||
| c0589cab05 | |||
| f35babad98 | |||
| 8369cb2af6 | |||
| 07d49b1bd8 | |||
| 5c8aab7917 | |||
| 14093c86a9 | |||
| e153df4d5b | |||
| 3e0c356b0b | |||
| 5fce74a2a6 | |||
| 7c418f7ea5 | |||
| dac72ff371 | |||
| baf22527b0 | |||
| 4cba60b27f | |||
| 5e539f71e6 | |||
| 9000407a77 | |||
| 4b6f8d2f62 | |||
| 688097faa9 | |||
| f08579d201 | |||
| 47fdaf6902 | |||
| 22c0f0d635 | |||
| a886e28f87 | |||
| 65104b5909 | |||
| 5605b4ee70 | |||
| 3ecba8e57f | |||
| 0c8bda4f41 | |||
| 458dbbd15d | |||
| b1aa87b515 | |||
| b637bd3866 | |||
| b4557789f8 | |||
| 63169d7679 | |||
| 5d705c0e9c | |||
| 682924b51a | |||
| 598c90f376 | |||
| e4962054a4 | |||
| 436c245d13 | |||
| 8891d85f75 | |||
| 763550c472 | |||
| d2ddb9882f | |||
| 64ac6a59c4 | |||
| acd9d6ff24 | |||
| f4b95c0226 | |||
| 177084b1f4 | |||
| aff0484ea2 | |||
| ef1eff6d48 | |||
| b407d12ed1 | |||
| e8bfce4ebd | |||
| be5f780977 | |||
| 9e39c8cd64 | |||
| 3356a76c9b | |||
| e29747170f | |||
| d6159a35aa | |||
| 0019ea28c3 | |||
| e899de88ff | |||
| bb111d325f | |||
| 73906f9f29 | |||
| b20d7ff48c | |||
| 8262d56574 | |||
| 257b9a2015 | |||
| ff07595519 | |||
| 6023794081 | |||
| a1189fa37a | |||
| 5a2a2f49ed | |||
| 6c372d1781 | |||
| 1669d0fdbd | |||
| 47b4c649f1 | |||
| ead67d8e67 | |||
| 8c4ba3257c | |||
| 05c4aa5017 | |||
| f84b2f072f | |||
| 9b122e5449 | |||
| 246b9f3182 | |||
| bf6854d121 | |||
| eebd48e51d | |||
| 9767cc84b0 | |||
| 11b0b6958f | |||
| cb1f30e9d2 | |||
| ec943b38a2 | |||
| 68f0393cf3 | |||
| fe26db4d36 | |||
| e3e4decabf | |||
| 6537909fd2 | |||
| 26ced19e18 | |||
| 3d85a89cc9 | |||
| 16944b0c4c | |||
| c4cc3ad81e | |||
| 0674a58502 | |||
| a809f33418 | |||
| bf33a1827c | |||
| 57ef6dae91 | |||
| 758f1bcdee | |||
| fa6fd5980e | |||
| 3762f1e50d | |||
| 5aaee670d1 | |||
| 9916f0e5c5 | |||
| 9eba0ac58c | |||
| 13493ec051 | |||
| b7a7a5c582 | |||
| 213527ca71 | |||
| dc7ac22f84 | |||
| 16d1577722 | |||
| bd6a7dea7e | |||
| 6433eebe1e | |||
| 35b7cf4c98 | |||
| c733d03525 | |||
| aef5113d49 | |||
| b21fce4d6f | |||
| be8d8ded3f | |||
| a5a2d43d7c | |||
| da878c36bd | |||
| 11e2965b5c | |||
| 4203f53b67 | |||
| d4c3905355 | |||
| 307da1fe39 | |||
| 44b2df27ea | |||
| 5ee0cea672 | |||
| df6607e236 | |||
| e32c49c12c | |||
| 8693031b06 | |||
| b644d5b2ae | |||
| 5e8852af80 | |||
| b6f8eface7 | |||
| fe23e0cc6e | |||
| bdca52a95d | |||
| 33d2618e5b | |||
| 2bc8723824 | |||
| 4ecf05aa13 | |||
| 1705638e25 | |||
| 5db0f47710 | |||
| ee173dc3a2 | |||
| 591a6c8671 | |||
| 6c189511af | |||
| a06223b844 | |||
| 9232ddf0ab | |||
| 1c14d872a8 | |||
| 98fc131d47 | |||
| 8ad31d2559 | |||
| 46dde3f8f7 |
+1
-1
@@ -5,7 +5,7 @@ IndentWidth: 1
|
||||
AccessModifierOffset: -1
|
||||
PointerAlignment: Left
|
||||
NamespaceIndentation: All
|
||||
ColumnLimit: 100
|
||||
ColumnLimit: 200
|
||||
BreakBeforeBraces: Allman
|
||||
BreakConstructorInitializersBeforeComma: true
|
||||
BreakBeforeBinaryOperators: false
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
[submodule "llvm"]
|
||||
path = llvm
|
||||
url = https://github.com/llvm-mirror/llvm
|
||||
branch = release_36
|
||||
branch = release_38
|
||||
[submodule "rsx_program_decompiler"]
|
||||
path = rsx_program_decompiler
|
||||
url = https://github.com/RPCS3/rsx_program_decompiler
|
||||
|
||||
+54
-48
@@ -1,90 +1,96 @@
|
||||
language: cpp
|
||||
sudo: required
|
||||
sudo: false
|
||||
dist: trusty
|
||||
|
||||
os:
|
||||
- linux
|
||||
# - osx
|
||||
- linux
|
||||
#- osx
|
||||
|
||||
osx_image: xcode6.4
|
||||
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
- clang
|
||||
- gcc
|
||||
|
||||
cache:
|
||||
ccache: true
|
||||
directories:
|
||||
- $HOME/hombebrew_cache
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: "Vf+FY48nip9JppMnq11105NealdErSWsoUhHo63/V3V+LKfA9guenxCp93/qoSIdSGC/sJwb0yIIMGvkTT/rxDJNh6Z+BWUTb2E0WEIIQbvTJNOSUzoq7dfF1LT61XjVjByFzcbC2xjtaBowmcAYEs1jGUUuEjYVCMmD5lY8hUg="
|
||||
# Which Travis environment to run Coverity on
|
||||
- coverity_scan_run_condition='"$TRAVIS_OS_NAME" = linux -a "$CC" = gcc'
|
||||
# Test mode is for testing if it's working with Coverity. Change to true if testing, to avoid reaching the quota.
|
||||
- coverity_scan_script_test_mode=false
|
||||
- secure: "Vf+FY48nip9JppMnq11105NealdErSWsoUhHo63/V3V+LKfA9guenxCp93/qoSIdSGC/sJwb0yIIMGvkTT/rxDJNh6Z+BWUTb2E0WEIIQbvTJNOSUzoq7dfF1LT61XjVjByFzcbC2xjtaBowmcAYEs1jGUUuEjYVCMmD5lY8hUg="
|
||||
# Which Travis environment to run Coverity on
|
||||
- coverity_scan_run_condition='"$TRAVIS_OS_NAME" = linux -a "$CC" = gcc'
|
||||
# Test mode is for testing if it's working with Coverity. Change to true if testing, to avoid reaching the quota.
|
||||
- coverity_scan_script_test_mode=false
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
exclude:
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
|
||||
git:
|
||||
submodules: false
|
||||
|
||||
before_install:
|
||||
# shutdown services on Travis, which may have a memory impact
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
sudo apt-get install -y libwxgtk3.0-dev;
|
||||
fi;
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$CXX" = "g++" ]; then
|
||||
export CXX="g++-5" CC="gcc-5" CXXFLAGS="-Wno-format-security";
|
||||
export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01';
|
||||
fi;
|
||||
# Add coverall for C++ so coverall.io could be triggered. Even it should be --coverage and gcov.
|
||||
# Add coverall for C++ so coverall.io could be triggered. Even it should be --coverage and gcov.
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
sudo pip install cpp-coveralls requests[security];
|
||||
pip install --user cpp-coveralls requests[security];
|
||||
else
|
||||
brew update; brew update;
|
||||
brew install glew wxwidgets llvm38;
|
||||
brew install ccache glew wxwidgets llvm38;
|
||||
fi;
|
||||
|
||||
before_script:
|
||||
- git submodule update --init rsx_program_decompiler asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng Utilities/yaml-cpp 3rdparty/cereal
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cmake ..; else cmake .. -DLLVM_DIR=/usr/local/opt/llvm38/lib/llvm-3.8/share/llvm/cmake; fi
|
||||
- git submodule update --init rsx_program_decompiler asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng Utilities/yaml-cpp 3rdparty/cereal
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
cmake ..;
|
||||
else
|
||||
cmake .. -DLLVM_DIR=/usr/local/opt/llvm38/lib/llvm-3.8/share/llvm/cmake;
|
||||
fi;
|
||||
|
||||
script:
|
||||
# Add a command to show all the variables. May be useful for debugging Travis.
|
||||
# - echo "--Shell Export Lists START--" ; export -p; echo "--Shell Export Lists STOP--";
|
||||
# And to ensure the versions of toolchain
|
||||
- echo "--CXX version?"; "$CXX" --version; echo "--CXX version confirmed";
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then make -j 4; fi
|
||||
# Add a command to show all the variables. May be useful for debugging Travis.
|
||||
#- echo "--Shell Export Lists START--" ; export -p; echo "--Shell Export Lists STOP--";
|
||||
# And to ensure the versions of toolchain
|
||||
- echo "--CXX version?"; "$CXX" --version; echo "--CXX version confirmed";
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then make -j 3; fi
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
# - llvm-toolchain-trusty-3.8 temporarily disabled
|
||||
- ubuntu-toolchain-r-test
|
||||
#- llvm-toolchain-trusty-3.8 temporarily disabled
|
||||
packages:
|
||||
- cmake
|
||||
- libopenal-dev
|
||||
- freeglut3-dev
|
||||
- libglew-dev
|
||||
- libc6-dev
|
||||
# - llvm-3.8
|
||||
# - llvm-3.8-dev
|
||||
- libedit-dev
|
||||
- g++-5
|
||||
- gcc-5
|
||||
# - clang-3.6
|
||||
- libstdc++-5-dev
|
||||
- lib32stdc++6
|
||||
- zlib1g-dev
|
||||
- cmake
|
||||
- libopenal-dev
|
||||
- freeglut3-dev
|
||||
- libglew-dev
|
||||
- libc6-dev
|
||||
#- llvm-3.8
|
||||
#- llvm-3.8-dev
|
||||
- libedit-dev
|
||||
- g++-5
|
||||
- gcc-5
|
||||
#- clang-3.6
|
||||
- libstdc++-5-dev
|
||||
- lib32stdc++6
|
||||
- zlib1g-dev
|
||||
- libwxgtk3.0-dev
|
||||
coverity_scan:
|
||||
project:
|
||||
name: $TRAVIS_REPO_SLUG
|
||||
description: "PS3 emulator/debugger"
|
||||
notification_email: raul.tambre@gmail.com
|
||||
build_command: "make -j 4"
|
||||
notification_email: nekotekina@gmail.com
|
||||
build_command: "make -j 3"
|
||||
branch_pattern: coverity_scan
|
||||
|
||||
after_success:
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "$TRAVIS_OS_NAME" = linux ]; then coveralls --extension .c --extension .cpp --extension .h; fi
|
||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "$TRAVIS_OS_NAME" = linux ]; then coveralls --extension .c --extension .cpp --extension .h; fi
|
||||
|
||||
@@ -19,6 +19,12 @@ if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 6.1)
|
||||
message( FATAL_ERROR "RPCS3 can't be compiled with gcc-6.1, see #1691." )
|
||||
endif()
|
||||
|
||||
find_program(CCACHE_FOUND ccache)
|
||||
if (CCACHE_FOUND)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
|
||||
endif()
|
||||
|
||||
add_definitions(-DCMAKE_BUILD)
|
||||
|
||||
# We use libpng's static library and don't need to build the shared library and run the tests
|
||||
|
||||
@@ -4,11 +4,10 @@ RPCS3
|
||||
[](https://travis-ci.org/RPCS3/rpcs3)
|
||||
[](https://ci.appveyor.com/project/rpcs3/rpcs3/branch/master)
|
||||
[](https://scan.coverity.com/projects/3960)
|
||||
[](https://coveralls.io/r/RPCS3/rpcs3)
|
||||
|
||||
An open-source PlayStation 3 emulator/debugger written in C++.
|
||||
|
||||
You can find some basic information in the [FAQ](https://github.com/RPCS3/rpcs3/wiki/FAQ). For discussion about this emulator and PS3 emulation please visit the [official forums](http://www.emunewz.net/forum/forumdisplay.php?fid=162).
|
||||
You can find some basic information in the [FAQ](https://github.com/RPCS3/rpcs3/wiki/FAQ). For discussion about this emulator and PS3 emulation please visit the [official forums](http://www.emunewz.net/forum/forumdisplay.php?fid=172).
|
||||
|
||||
Official Discord server: https://discord.me/RPCS3
|
||||
|
||||
|
||||
+13
-7
@@ -300,21 +300,24 @@ struct atomic_storage<T, 2> : atomic_storage<T, 0>
|
||||
static inline bool bts(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
__asm__("lock btsw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
|
||||
ushort _bit = (ushort)bit;
|
||||
__asm__("lock btsw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline bool btr(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
__asm__("lock btrw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
|
||||
ushort _bit = (ushort)bit;
|
||||
__asm__("lock btrw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline bool btc(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
__asm__("lock btcw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
|
||||
ushort _bit = (ushort)bit;
|
||||
__asm__("lock btcw %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
@@ -498,21 +501,24 @@ struct atomic_storage<T, 8> : atomic_storage<T, 0>
|
||||
static inline bool bts(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
__asm__("lock btsq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
|
||||
ullong _bit = bit;
|
||||
__asm__("lock btsq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline bool btr(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
__asm__("lock btrq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
|
||||
ullong _bit = bit;
|
||||
__asm__("lock btrq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline bool btc(T& dest, uint bit)
|
||||
{
|
||||
bool result;
|
||||
__asm__("lock btcq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (bit) : "cc");
|
||||
ullong _bit = bit;
|
||||
__asm__("lock btcq %2, %0\n" "setc %1" : "+m" (dest), "=r" (result) : "Ir" (_bit) : "cc");
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
@@ -530,7 +536,7 @@ struct atomic_storage<T, 16> : atomic_storage<T, 0>
|
||||
|
||||
static inline T load(const T& dest)
|
||||
{
|
||||
llong result[2];
|
||||
llong result[2]{0, 0};
|
||||
_InterlockedCompareExchange128((volatile llong*)&dest, 0, 0, result);
|
||||
return *(T*)+result;
|
||||
}
|
||||
|
||||
+21
-18
@@ -7,6 +7,7 @@ struct bf_base
|
||||
{
|
||||
using type = T;
|
||||
using vtype = simple_t<type>;
|
||||
using utype = typename std::make_unsigned<vtype>::type;
|
||||
|
||||
// Datatype bitsize
|
||||
static constexpr uint bitmax = sizeof(T) * 8; static_assert(N - 1 < bitmax, "bf_base<> error: N out of bounds");
|
||||
@@ -15,7 +16,7 @@ struct bf_base
|
||||
static constexpr uint bitsize = N;
|
||||
|
||||
// Value mask
|
||||
static constexpr vtype vmask = static_cast<vtype>(~std::make_unsigned_t<vtype>{} >> (bitmax - bitsize));
|
||||
static constexpr utype vmask = static_cast<utype>(~utype{} >> (bitmax - bitsize));
|
||||
|
||||
protected:
|
||||
type m_data;
|
||||
@@ -27,14 +28,15 @@ struct bf_t : bf_base<T, N>
|
||||
{
|
||||
using type = typename bf_t::type;
|
||||
using vtype = typename bf_t::vtype;
|
||||
using utype = typename bf_t::utype;
|
||||
|
||||
// Field offset
|
||||
static constexpr uint bitpos = I; static_assert(bitpos + N <= bf_t::bitmax, "bf_t<> error: I out of bounds");
|
||||
|
||||
// Get bitmask of size N, at I pos
|
||||
static constexpr vtype data_mask()
|
||||
static constexpr utype data_mask()
|
||||
{
|
||||
return bf_t::vmask << bitpos;
|
||||
return static_cast<utype>(~utype{} >> (bf_t::bitmax - bf_t::bitsize) << bitpos);
|
||||
}
|
||||
|
||||
// Bitfield extraction helper
|
||||
@@ -50,7 +52,7 @@ struct bf_t : bf_base<T, N>
|
||||
// Load unsigned value
|
||||
static constexpr T2 extract(const T& data)
|
||||
{
|
||||
return (data >> bitpos) & bf_t::vmask;
|
||||
return static_cast<T2>((static_cast<utype>(data) >> bitpos) & bf_t::vmask);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -60,7 +62,7 @@ struct bf_t : bf_base<T, N>
|
||||
// Load signed value (sign-extended)
|
||||
static constexpr T2 extract(const T& data)
|
||||
{
|
||||
return data << (bf_t::bitmax - bitpos - N) >> (bf_t::bitmax - N);
|
||||
return static_cast<T2>(static_cast<vtype>(static_cast<utype>(data) << (bf_t::bitmax - bitpos - N)) >> (bf_t::bitmax - N));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -73,7 +75,7 @@ struct bf_t : bf_base<T, N>
|
||||
// Bitfield insertion
|
||||
static constexpr vtype insert(vtype value)
|
||||
{
|
||||
return (value & bf_t::vmask) << bitpos;
|
||||
return static_cast<vtype>((value & bf_t::vmask) << bitpos);
|
||||
}
|
||||
|
||||
// Load bitfield value
|
||||
@@ -85,7 +87,7 @@ struct bf_t : bf_base<T, N>
|
||||
// Load raw data with mask applied
|
||||
constexpr T unshifted() const
|
||||
{
|
||||
return this->m_data & data_mask();
|
||||
return static_cast<T>(this->m_data & data_mask());
|
||||
}
|
||||
|
||||
// Optimized bool conversion (must be removed if inappropriate)
|
||||
@@ -97,14 +99,14 @@ struct bf_t : bf_base<T, N>
|
||||
// Store bitfield value
|
||||
bf_t& operator =(vtype value)
|
||||
{
|
||||
this->m_data = (this->m_data & ~data_mask()) | insert(value);
|
||||
this->m_data = static_cast<vtype>((this->m_data & ~data_mask()) | insert(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
vtype operator ++(int)
|
||||
{
|
||||
vtype result = *this;
|
||||
*this = result + 1;
|
||||
utype result = *this;
|
||||
*this = static_cast<vtype>(result + 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -115,8 +117,8 @@ struct bf_t : bf_base<T, N>
|
||||
|
||||
vtype operator --(int)
|
||||
{
|
||||
vtype result = *this;
|
||||
*this = result - 1;
|
||||
utype result = *this;
|
||||
*this = static_cast<vtype>(result - 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -142,19 +144,19 @@ struct bf_t : bf_base<T, N>
|
||||
|
||||
bf_t& operator &=(vtype right)
|
||||
{
|
||||
this->m_data &= (right & bf_t::vmask) << bitpos;
|
||||
this->m_data &= static_cast<vtype>((static_cast<utype>(right) & bf_t::vmask) << bitpos);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bf_t& operator |=(vtype right)
|
||||
{
|
||||
this->m_data |= (right & bf_t::vmask) << bitpos;
|
||||
this->m_data |= static_cast<vtype>((static_cast<utype>(right) & bf_t::vmask) << bitpos);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bf_t& operator ^=(vtype right)
|
||||
{
|
||||
this->m_data ^= (right & bf_t::vmask) << bitpos;
|
||||
this->m_data ^= static_cast<vtype>((static_cast<utype>(right) & bf_t::vmask) << bitpos);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
@@ -165,23 +167,24 @@ struct cf_t : bf_base<typename F::type, F::bitsize + cf_t<Fields...>::bitsize>
|
||||
{
|
||||
using type = typename cf_t::type;
|
||||
using vtype = typename cf_t::vtype;
|
||||
using utype = typename cf_t::utype;
|
||||
|
||||
// Get disjunction of all "data" masks of concatenated values
|
||||
static constexpr vtype data_mask()
|
||||
{
|
||||
return F::data_mask() | cf_t<Fields...>::data_mask();
|
||||
return static_cast<vtype>(F::data_mask() | cf_t<Fields...>::data_mask());
|
||||
}
|
||||
|
||||
// Extract all bitfields and concatenate
|
||||
static constexpr vtype extract(const type& data)
|
||||
{
|
||||
return F::extract(data) << cf_t<Fields...>::bitsize | cf_t<Fields...>::extract(data);
|
||||
return static_cast<vtype>(static_cast<utype>(F::extract(data)) << cf_t<Fields...>::bitsize | cf_t<Fields...>::extract(data));
|
||||
}
|
||||
|
||||
// Split bitfields and insert them
|
||||
static constexpr vtype insert(vtype value)
|
||||
{
|
||||
return F::insert(value >> cf_t<Fields...>::bitsize) | cf_t<Fields...>::insert(value);
|
||||
return static_cast<vtype>(F::insert(value >> cf_t<Fields...>::bitsize) | cf_t<Fields...>::insert(value));
|
||||
}
|
||||
|
||||
// Load value
|
||||
|
||||
@@ -261,6 +261,22 @@ void cfg::node::from_default()
|
||||
}
|
||||
}
|
||||
|
||||
void cfg::bool_entry::from_default()
|
||||
{
|
||||
value = def;
|
||||
}
|
||||
|
||||
void cfg::string_entry::from_default()
|
||||
{
|
||||
*this = def;
|
||||
}
|
||||
|
||||
void cfg::set_entry::from_default()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_set = {};
|
||||
}
|
||||
|
||||
cfg::root_node& cfg::get_root()
|
||||
{
|
||||
// Magic static
|
||||
|
||||
+3
-13
@@ -141,10 +141,7 @@ namespace cfg
|
||||
return *this;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
value = def;
|
||||
}
|
||||
void from_default() override;
|
||||
|
||||
std::string to_string() const override
|
||||
{
|
||||
@@ -428,10 +425,7 @@ namespace cfg
|
||||
return m_value.size();
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
*this = def;
|
||||
}
|
||||
void from_default() override;
|
||||
|
||||
std::string to_string() const override
|
||||
{
|
||||
@@ -473,11 +467,7 @@ namespace cfg
|
||||
m_set = std::move(set);
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_set = {};
|
||||
}
|
||||
void from_default() override;
|
||||
|
||||
std::vector<std::string> to_list() const override
|
||||
{
|
||||
|
||||
+107
-76
@@ -1,6 +1,6 @@
|
||||
#include "File.h"
|
||||
#include "mutex.h"
|
||||
#include "StrFmt.h"
|
||||
#include "SharedMutex.h"
|
||||
#include "BEType.h"
|
||||
#include "Crypto/sha1.h"
|
||||
|
||||
@@ -69,12 +69,22 @@ static time_t to_time(const FILETIME& ft)
|
||||
return to_time(v);
|
||||
}
|
||||
|
||||
static FILETIME from_time(s64 _time)
|
||||
{
|
||||
const ullong wtime = (_time + 11644473600ULL) * 10000000ULL;
|
||||
FILETIME result;
|
||||
result.dwLowDateTime = static_cast<DWORD>(wtime);
|
||||
result.dwHighDateTime = static_cast<DWORD>(wtime >> 32);
|
||||
return result;
|
||||
}
|
||||
|
||||
static fs::error to_error(DWORD e)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case ERROR_FILE_NOT_FOUND: return fs::error::noent;
|
||||
case ERROR_PATH_NOT_FOUND: return fs::error::noent;
|
||||
case ERROR_ACCESS_DENIED: return fs::error::acces;
|
||||
case ERROR_ALREADY_EXISTS: return fs::error::exist;
|
||||
case ERROR_FILE_EXISTS: return fs::error::exist;
|
||||
case ERROR_NEGATIVE_SEEK: return fs::error::inval;
|
||||
@@ -94,6 +104,7 @@ static fs::error to_error(DWORD e)
|
||||
#include <libgen.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#include <copyfile.h>
|
||||
@@ -109,6 +120,7 @@ static fs::error to_error(int e)
|
||||
case ENOENT: return fs::error::noent;
|
||||
case EEXIST: return fs::error::exist;
|
||||
case EINVAL: return fs::error::inval;
|
||||
case EACCES: return fs::error::acces;
|
||||
default: fmt::throw_exception("Unknown system error: %d.", e);
|
||||
}
|
||||
}
|
||||
@@ -136,6 +148,18 @@ namespace fs
|
||||
static device_manager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
file_base::~file_base()
|
||||
{
|
||||
}
|
||||
|
||||
dir_base::~dir_base()
|
||||
{
|
||||
}
|
||||
|
||||
device_base::~device_base()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<fs::device_base> fs::device_manager::get_device(const std::string& path)
|
||||
@@ -591,6 +615,48 @@ bool fs::truncate_file(const std::string& path, u64 length)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool fs::utime(const std::string& path, s64 atime, s64 mtime)
|
||||
{
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
return device->utime(path, atime, mtime);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Open the file
|
||||
const auto handle = CreateFileW(to_wchar(path).get(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
g_tls_error = to_error(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
FILETIME _atime = from_time(atime);
|
||||
FILETIME _mtime = from_time(mtime);
|
||||
if (!SetFileTime(handle, nullptr, &_atime, &_mtime))
|
||||
{
|
||||
g_tls_error = to_error(GetLastError());
|
||||
CloseHandle(handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
CloseHandle(handle);
|
||||
return true;
|
||||
#else
|
||||
::utimbuf buf;
|
||||
buf.actime = atime;
|
||||
buf.modtime = mtime;
|
||||
|
||||
if (::utime(path.c_str(), &buf) != 0)
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void fs::file::xnull() const
|
||||
{
|
||||
fmt::throw_exception<std::logic_error>("fs::file is null");
|
||||
@@ -601,17 +667,17 @@ void fs::file::xfail() const
|
||||
fmt::throw_exception("Unexpected fs::error %s", g_tls_error);
|
||||
}
|
||||
|
||||
bool fs::file::open(const std::string& path, bs_t<open_mode> mode)
|
||||
fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
{
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
if (auto&& _file = device->open(path, mode))
|
||||
{
|
||||
m_file = std::move(_file);
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -631,7 +697,7 @@ bool fs::file::open(const std::string& path, bs_t<open_mode> mode)
|
||||
if (test(mode & fs::excl))
|
||||
{
|
||||
g_tls_error = error::inval;
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
disp = test(mode & fs::trunc) ? TRUNCATE_EXISTING : OPEN_EXISTING;
|
||||
@@ -642,7 +708,7 @@ bool fs::file::open(const std::string& path, bs_t<open_mode> mode)
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
g_tls_error = to_error(GetLastError());
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
class windows_file final : public file_base
|
||||
@@ -763,7 +829,7 @@ bool fs::file::open(const std::string& path, bs_t<open_mode> mode)
|
||||
if (fd == -1)
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
class unix_file final : public file_base
|
||||
@@ -849,8 +915,6 @@ bool fs::file::open(const std::string& path, bs_t<open_mode> mode)
|
||||
|
||||
m_file = std::make_unique<unix_file>(fd);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fs::file::file(const void* ptr, std::size_t size)
|
||||
@@ -871,36 +935,42 @@ fs::file::file(const void* ptr, std::size_t size)
|
||||
|
||||
fs::stat_t stat() override
|
||||
{
|
||||
fmt::throw_exception<std::logic_error>("Not supported" HERE);
|
||||
fmt::raw_error("fs::file::memory_stream::stat(): not supported");
|
||||
}
|
||||
|
||||
bool trunc(u64 length) override
|
||||
{
|
||||
fmt::throw_exception<std::logic_error>("Not allowed" HERE);
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 read(void* buffer, u64 count) override
|
||||
{
|
||||
const u64 start = m_pos;
|
||||
const u64 end = seek(count, fs::seek_cur);
|
||||
if (end < start) fmt::throw_exception<std::logic_error>("Stream overflow" HERE);
|
||||
const u64 read_size = end - start;
|
||||
std::memcpy(buffer, m_ptr + start, read_size);
|
||||
return read_size;
|
||||
if (m_pos < m_size)
|
||||
{
|
||||
// Get readable size
|
||||
if (const u64 result = std::min<u64>(count, m_size - m_pos))
|
||||
{
|
||||
std::memcpy(buffer, m_ptr + m_pos, result);
|
||||
m_pos += result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 write(const void* buffer, u64 count) override
|
||||
{
|
||||
fmt::throw_exception<std::logic_error>("Not allowed" HERE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 seek(s64 offset, fs::seek_mode whence) override
|
||||
{
|
||||
return
|
||||
whence == fs::seek_set ? m_pos = std::min<u64>(offset, m_size) :
|
||||
whence == fs::seek_cur ? m_pos = std::min<u64>(offset + m_pos, m_size) :
|
||||
whence == fs::seek_end ? m_pos = std::min<u64>(offset + m_size, m_size) :
|
||||
(fmt::throw_exception("Invalid whence (0x%x)" HERE, whence), 0);
|
||||
whence == fs::seek_set ? m_pos = offset :
|
||||
whence == fs::seek_cur ? m_pos = offset + m_pos :
|
||||
whence == fs::seek_end ? m_pos = offset + m_size :
|
||||
(fmt::raw_error("fs::file::memory_stream::seek(): invalid whence"), 0);
|
||||
}
|
||||
|
||||
u64 size() override
|
||||
@@ -1055,11 +1125,22 @@ const std::string& fs::get_config_dir()
|
||||
// Use magic static
|
||||
static const std::string s_dir = []
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return get_executable_dir(); // ?
|
||||
#else
|
||||
std::string dir;
|
||||
|
||||
#ifdef _WIN32
|
||||
wchar_t buf[2048];
|
||||
if (GetModuleFileName(NULL, buf, ::size32(buf)) - 1 >= ::size32(buf) - 1)
|
||||
{
|
||||
MessageBoxA(0, fmt::format("GetModuleFileName() failed: error %u.", GetLastError()).c_str(), "fs::get_config_dir()", MB_ICONERROR);
|
||||
return dir; // empty
|
||||
}
|
||||
|
||||
to_utf8(dir, buf); // Convert to UTF-8
|
||||
|
||||
std::replace(dir.begin(), dir.end(), '\\', '/');
|
||||
|
||||
dir.resize(dir.rfind('/') + 1);
|
||||
#else
|
||||
if (const char* home = ::getenv("XDG_CONFIG_HOME"))
|
||||
dir = home;
|
||||
else if (const char* home = ::getenv("HOME"))
|
||||
@@ -1072,59 +1153,9 @@ const std::string& fs::get_config_dir()
|
||||
if (!is_dir(dir) && !create_path(dir))
|
||||
{
|
||||
std::printf("Failed to create configuration directory '%s' (%d).\n", dir.c_str(), errno);
|
||||
return get_executable_dir();
|
||||
}
|
||||
|
||||
return dir;
|
||||
#endif
|
||||
}();
|
||||
|
||||
return s_dir;
|
||||
}
|
||||
|
||||
const std::string& fs::get_executable_dir()
|
||||
{
|
||||
// Use magic static
|
||||
static const std::string s_dir = []
|
||||
{
|
||||
std::string dir;
|
||||
|
||||
#ifdef _WIN32
|
||||
wchar_t buf[2048];
|
||||
if (GetModuleFileName(NULL, buf, ::size32(buf)) - 1 >= ::size32(buf) - 1)
|
||||
{
|
||||
MessageBoxA(0, fmt::format("GetModuleFileName() failed: error %u.", GetLastError()).c_str(), "fs::get_executable_dir()", MB_ICONERROR);
|
||||
return dir; // empty
|
||||
}
|
||||
|
||||
to_utf8(dir, buf); // Convert to UTF-8
|
||||
|
||||
std::replace(dir.begin(), dir.end(), '\\', '/');
|
||||
|
||||
#elif __APPLE__
|
||||
char buf[4096];
|
||||
u32 size = sizeof(buf);
|
||||
if (_NSGetExecutablePath(buf, &size))
|
||||
{
|
||||
std::printf("_NSGetExecutablePath() failed (size=0x%x).\n", size);
|
||||
return dir; // empty
|
||||
}
|
||||
|
||||
dir = buf;
|
||||
#else
|
||||
char buf[4096];
|
||||
const auto size = ::readlink("/proc/self/exe", buf, sizeof(buf));
|
||||
if (size <= 0 || size >= sizeof(buf))
|
||||
{
|
||||
std::printf("readlink(/proc/self/exe) failed (%d).\n", errno);
|
||||
return dir; // empty
|
||||
}
|
||||
|
||||
dir.assign(buf, size);
|
||||
#endif
|
||||
|
||||
// Leave only path
|
||||
dir.resize(dir.rfind('/') + 1);
|
||||
return dir;
|
||||
}();
|
||||
|
||||
@@ -1135,7 +1166,7 @@ std::string fs::get_data_dir(const std::string& prefix, const std::string& locat
|
||||
{
|
||||
static const std::string s_dir = []
|
||||
{
|
||||
const std::string& dir = get_config_dir() + "/data/";
|
||||
const std::string& dir = get_config_dir() + "data/";
|
||||
|
||||
if (!is_dir(dir) && !create_path(dir))
|
||||
{
|
||||
|
||||
+117
-13
@@ -6,6 +6,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace fs
|
||||
{
|
||||
@@ -57,7 +58,7 @@ namespace fs
|
||||
// File handle base
|
||||
struct file_base
|
||||
{
|
||||
virtual ~file_base() = default;
|
||||
virtual ~file_base();
|
||||
|
||||
virtual stat_t stat() = 0;
|
||||
virtual bool trunc(u64 length) = 0;
|
||||
@@ -76,7 +77,7 @@ namespace fs
|
||||
// Directory handle base
|
||||
struct dir_base
|
||||
{
|
||||
virtual ~dir_base() = default;
|
||||
virtual ~dir_base();
|
||||
|
||||
virtual bool read(dir_entry&) = 0;
|
||||
virtual void rewind() = 0;
|
||||
@@ -85,7 +86,7 @@ namespace fs
|
||||
// Virtual device
|
||||
struct device_base
|
||||
{
|
||||
virtual ~device_base() = default;
|
||||
virtual ~device_base();
|
||||
|
||||
virtual bool stat(const std::string& path, stat_t& info) = 0;
|
||||
virtual bool remove_dir(const std::string& path) = 0;
|
||||
@@ -93,6 +94,7 @@ namespace fs
|
||||
virtual bool rename(const std::string& from, const std::string& to) = 0;
|
||||
virtual bool remove(const std::string& path) = 0;
|
||||
virtual bool trunc(const std::string& path, u64 length) = 0;
|
||||
virtual bool utime(const std::string& path, s64 atime, s64 mtime) = 0;
|
||||
|
||||
virtual std::unique_ptr<file_base> open(const std::string& path, bs_t<open_mode> mode) = 0;
|
||||
virtual std::unique_ptr<dir_base> open_dir(const std::string& path) = 0;
|
||||
@@ -140,6 +142,9 @@ namespace fs
|
||||
// Change file size (possibly appending zeros)
|
||||
bool truncate_file(const std::string& path, u64 length);
|
||||
|
||||
// Set file access/modification time
|
||||
bool utime(const std::string& path, s64 atime, s64 mtime);
|
||||
|
||||
class file final
|
||||
{
|
||||
std::unique_ptr<file_base> m_file;
|
||||
@@ -152,17 +157,19 @@ namespace fs
|
||||
file() = default;
|
||||
|
||||
// Open file with specified mode
|
||||
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read)
|
||||
{
|
||||
open(path, mode);
|
||||
}
|
||||
|
||||
// Open file with specified mode
|
||||
bool open(const std::string& path, bs_t<open_mode> mode = ::fs::read);
|
||||
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read);
|
||||
|
||||
// Open memory for read
|
||||
explicit file(const void* ptr, std::size_t size);
|
||||
|
||||
// Open file with specified args (forward to constructor)
|
||||
template <typename... Args>
|
||||
bool open(Args&&... args)
|
||||
{
|
||||
*this = fs::file(std::forward<Args>(args)...);
|
||||
return m_file.operator bool();
|
||||
}
|
||||
|
||||
// Check whether the handle is valid (opened file)
|
||||
explicit operator bool() const
|
||||
{
|
||||
@@ -437,9 +444,6 @@ namespace fs
|
||||
// Get configuration directory
|
||||
const std::string& get_config_dir();
|
||||
|
||||
// Get executable directory
|
||||
const std::string& get_executable_dir();
|
||||
|
||||
// Get data/cache directory for specified prefix and suffix
|
||||
std::string get_data_dir(const std::string& prefix, const std::string& location, const std::string& suffix);
|
||||
|
||||
@@ -459,8 +463,108 @@ namespace fs
|
||||
inval,
|
||||
noent,
|
||||
exist,
|
||||
acces,
|
||||
};
|
||||
|
||||
// Error code returned
|
||||
extern thread_local error g_tls_error;
|
||||
|
||||
template <typename T>
|
||||
struct container_stream final : file_base
|
||||
{
|
||||
// T can be a reference, but this is not recommended
|
||||
using value_type = typename std::remove_reference_t<T>::value_type;
|
||||
|
||||
T obj;
|
||||
u64 pos;
|
||||
|
||||
container_stream(T&& obj)
|
||||
: obj(std::forward<T>(obj))
|
||||
, pos(0)
|
||||
{
|
||||
}
|
||||
|
||||
~container_stream() override
|
||||
{
|
||||
}
|
||||
|
||||
stat_t stat() override
|
||||
{
|
||||
fmt::raw_error("fs::container_stream<>::stat(): not supported");
|
||||
}
|
||||
|
||||
bool trunc(u64 length) override
|
||||
{
|
||||
obj.resize(length);
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 read(void* buffer, u64 size) override
|
||||
{
|
||||
const u64 end = obj.size();
|
||||
|
||||
if (pos < end)
|
||||
{
|
||||
// Get readable size
|
||||
if (const u64 max = std::min<u64>(size, end - pos))
|
||||
{
|
||||
std::copy(obj.cbegin() + pos, obj.cbegin() + pos + max, static_cast<value_type*>(buffer));
|
||||
pos = pos + max;
|
||||
return max;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 write(const void* buffer, u64 size) override
|
||||
{
|
||||
const u64 old_size = obj.size();
|
||||
|
||||
if (old_size + size < old_size)
|
||||
{
|
||||
fmt::raw_error("fs::container_stream<>::write(): overflow");
|
||||
}
|
||||
|
||||
if (pos > old_size)
|
||||
{
|
||||
// Fill gap if necessary (default-initialized)
|
||||
obj.resize(pos);
|
||||
}
|
||||
|
||||
const auto src = static_cast<const value_type*>(buffer);
|
||||
|
||||
// Overwrite existing part
|
||||
const u64 overlap = std::min<u64>(obj.size() - pos, size);
|
||||
std::copy(src, src + overlap, obj.begin() + pos);
|
||||
|
||||
// Append new data
|
||||
obj.insert(obj.end(), src + overlap, src + size);
|
||||
pos += size;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
u64 seek(s64 offset, seek_mode whence) override
|
||||
{
|
||||
return
|
||||
whence == fs::seek_set ? pos = offset :
|
||||
whence == fs::seek_cur ? pos = offset + pos :
|
||||
whence == fs::seek_end ? pos = offset + size() :
|
||||
(fmt::raw_error("fs::container_stream<>::seek(): invalid whence"), 0);
|
||||
}
|
||||
|
||||
u64 size() override
|
||||
{
|
||||
return obj.size();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
file make_stream(T&& container = T{})
|
||||
{
|
||||
file result;
|
||||
result.reset(std::make_unique<container_stream<T>>(std::forward<T>(container)));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+110
-45
@@ -68,16 +68,16 @@ static u8* s_unwind_info;
|
||||
static u64 s_unwind_size;
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::vector<RUNTIME_FUNCTION> s_unwind; // Custom .pdata section replacement
|
||||
static std::vector<std::vector<RUNTIME_FUNCTION>> s_unwind; // .pdata
|
||||
#endif
|
||||
|
||||
// Helper class
|
||||
struct MemoryManager final : llvm::RTDyldMemoryManager
|
||||
{
|
||||
std::unordered_map<std::string, std::uintptr_t> table;
|
||||
std::unordered_map<std::string, std::uintptr_t>& m_link;
|
||||
|
||||
MemoryManager(std::unordered_map<std::string, std::uintptr_t>&& table)
|
||||
: table(std::move(table))
|
||||
MemoryManager(std::unordered_map<std::string, std::uintptr_t>& table)
|
||||
: m_link(table)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -88,22 +88,22 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
||||
|
||||
virtual u64 getSymbolAddress(const std::string& name) override
|
||||
{
|
||||
if (u64 addr = RTDyldMemoryManager::getSymbolAddress(name))
|
||||
{
|
||||
// This may be bad if LLVM requests some built-in functions like fma.
|
||||
LOG_ERROR(GENERAL, "LLVM: Symbol requested %s -> 0x%016llx", name, addr);
|
||||
return addr;
|
||||
}
|
||||
const auto found = m_link.find(name);
|
||||
|
||||
const auto found = table.find(name);
|
||||
|
||||
if (found != table.end())
|
||||
if (found != m_link.end())
|
||||
{
|
||||
return found->second;
|
||||
}
|
||||
|
||||
if (u64 addr = RTDyldMemoryManager::getSymbolAddress(name))
|
||||
{
|
||||
// This may be bad if LLVM requests some built-in functions like fma.
|
||||
LOG_ERROR(GENERAL, "LLVM: Symbol requested: %s -> 0x%016llx", name, addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
// It's fine if some function is never called, for example.
|
||||
LOG_ERROR(GENERAL, "LLVM: Linkage failed for %s", name);
|
||||
LOG_ERROR(GENERAL, "LLVM: Symbol not found: %s", name);
|
||||
return (u64)null;
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
||||
s_code_addr = (u8*)m_next;
|
||||
s_code_size = size;
|
||||
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Code section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x)", sec_id, sec_name.data(), m_next, size, align);
|
||||
LOG_NOTICE(GENERAL, "LLVM: Code section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x)", sec_id, sec_name.data(), m_next, size, align);
|
||||
return (u8*)std::exchange(m_next, (void*)next);
|
||||
}
|
||||
|
||||
@@ -161,21 +161,21 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Data section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x, %s)", sec_id, sec_name.data(), m_next, size, align, is_ro ? "ro" : "rw");
|
||||
LOG_NOTICE(GENERAL, "LLVM: Data section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x, %s)", sec_id, sec_name.data(), m_next, size, align, is_ro ? "ro" : "rw");
|
||||
return (u8*)std::exchange(m_next, (void*)next);
|
||||
}
|
||||
|
||||
virtual bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
// TODO: make only read-only sections read-only
|
||||
#ifdef _WIN32
|
||||
DWORD op;
|
||||
VirtualProtect(s_memory, (u64)m_next - (u64)s_memory, PAGE_READONLY, &op);
|
||||
VirtualProtect(s_code_addr, s_code_size, PAGE_EXECUTE_READ, &op);
|
||||
#else
|
||||
::mprotect(s_memory, (u64)m_next - (u64)s_memory, PROT_READ);
|
||||
::mprotect(s_code_addr, s_code_size, PROT_READ | PROT_EXEC);
|
||||
#endif
|
||||
//#ifdef _WIN32
|
||||
// DWORD op;
|
||||
// VirtualProtect(s_memory, (u64)m_next - (u64)s_memory, PAGE_READONLY, &op);
|
||||
// VirtualProtect(s_code_addr, s_code_size, PAGE_EXECUTE_READ, &op);
|
||||
//#else
|
||||
// ::mprotect(s_memory, (u64)m_next - (u64)s_memory, PROT_READ);
|
||||
// ::mprotect(s_code_addr, s_code_size, PROT_READ | PROT_EXEC);
|
||||
//#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -197,19 +197,24 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
||||
~MemoryManager()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (!RtlDeleteFunctionTable(s_unwind.data()))
|
||||
for (auto&& unwind : s_unwind)
|
||||
{
|
||||
LOG_FATAL(GENERAL, "RtlDeleteFunctionTable(%p) failed! Error %u", s_unwind_info, GetLastError());
|
||||
if (!RtlDeleteFunctionTable(unwind.data()))
|
||||
{
|
||||
LOG_FATAL(GENERAL, "RtlDeleteFunctionTable() failed! Error %u", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
s_unwind.clear();
|
||||
|
||||
if (!VirtualFree(s_memory, 0, MEM_DECOMMIT))
|
||||
{
|
||||
LOG_FATAL(GENERAL, "VirtualFree(%p) failed! Error %u", s_memory, GetLastError());
|
||||
}
|
||||
#else
|
||||
if (::mprotect(s_memory, s_memory_size, PROT_NONE))
|
||||
if (!::mmap(s_memory, s_memory_size, PROT_NONE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0))
|
||||
{
|
||||
LOG_FATAL(GENERAL, "mprotect(%p) failed! Error %d", s_memory, errno);
|
||||
LOG_FATAL(GENERAL, "mmap(%p) failed! Error %d", s_memory, errno);
|
||||
}
|
||||
|
||||
// TODO: unregister EH frames if necessary
|
||||
@@ -223,36 +228,52 @@ private:
|
||||
// Helper class
|
||||
struct EventListener final : llvm::JITEventListener
|
||||
{
|
||||
std::string path;
|
||||
|
||||
virtual void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
|
||||
{
|
||||
const llvm::StringRef elf = obj.getData();
|
||||
fs::file(fs::get_config_dir() + "LLVM.obj", fs::rewrite)
|
||||
.write(elf.data(), elf.size());
|
||||
if (!path.empty())
|
||||
{
|
||||
const llvm::StringRef elf = obj.getData();
|
||||
fs::file(path, fs::rewrite).write(elf.data(), elf.size());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static EventListener s_listener;
|
||||
|
||||
jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unordered_map<std::string, std::uintptr_t>&& table)
|
||||
static void dummy()
|
||||
{
|
||||
}
|
||||
|
||||
jit_compiler::jit_compiler(std::unordered_map<std::string, std::uintptr_t> init_linkage_info)
|
||||
: m_link(std::move(init_linkage_info))
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
m_link.emplace("__chkstk", (u64)&dummy);
|
||||
#endif
|
||||
|
||||
verify(HERE), s_memory;
|
||||
|
||||
std::string result;
|
||||
|
||||
const auto module_ptr = _module.get();
|
||||
|
||||
// Initialization
|
||||
llvm::InitializeNativeTarget();
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
LLVMLinkInMCJIT();
|
||||
const auto _cpu = llvm::sys::getHostCPUName();
|
||||
m_cpu = llvm::sys::getHostCPUName();
|
||||
|
||||
m_engine.reset(llvm::EngineBuilder(std::move(_module))
|
||||
if (m_cpu == "skylake")
|
||||
{
|
||||
m_cpu = "haswell";
|
||||
}
|
||||
|
||||
std::string result;
|
||||
|
||||
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("", g_llvm_ctx))
|
||||
.setErrorStr(&result)
|
||||
.setMCJITMemoryManager(std::make_unique<MemoryManager>(std::move(table)))
|
||||
.setMCJITMemoryManager(std::make_unique<MemoryManager>(m_link))
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setCodeModel((u64)s_memory <= 0x60000000 ? llvm::CodeModel::Small : llvm::CodeModel::Large) // TODO
|
||||
.setMCPU(_cpu == "skylake" ? "haswell" : _cpu)
|
||||
.setMCPU(m_cpu)
|
||||
.create());
|
||||
|
||||
if (!m_engine)
|
||||
@@ -262,8 +283,45 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
|
||||
|
||||
m_engine->setProcessAllSections(true); // ???
|
||||
m_engine->RegisterJITEventListener(&s_listener);
|
||||
}
|
||||
|
||||
void jit_compiler::load(std::unique_ptr<llvm::Module> module, std::unique_ptr<llvm::object::ObjectFile> object)
|
||||
{
|
||||
s_listener.path.clear();
|
||||
|
||||
auto* module_ptr = module.get();
|
||||
|
||||
m_engine->addModule(std::move(module));
|
||||
m_engine->addObjectFile(std::move(object));
|
||||
m_engine->finalizeObject();
|
||||
|
||||
m_map.clear();
|
||||
|
||||
for (auto& func : module_ptr->functions())
|
||||
{
|
||||
const std::string& name = func.getName();
|
||||
|
||||
if (!m_link.count(name))
|
||||
{
|
||||
// Register compiled function
|
||||
m_map[name] = m_engine->getFunctionAddress(name);
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
void jit_compiler::make(std::unique_ptr<llvm::Module> module, std::string path)
|
||||
{
|
||||
s_listener.path = std::move(path);
|
||||
|
||||
auto* module_ptr = module.get();
|
||||
|
||||
m_engine->addModule(std::move(module));
|
||||
m_engine->finalizeObject();
|
||||
|
||||
m_map.clear();
|
||||
|
||||
for (auto& func : module_ptr->functions())
|
||||
{
|
||||
if (!func.empty())
|
||||
@@ -278,6 +336,11 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
|
||||
func.deleteBody();
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
void jit_compiler::init()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Register .xdata UNWIND_INFO (.pdata section is empty for some reason)
|
||||
std::set<u64> func_set;
|
||||
@@ -290,8 +353,8 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
|
||||
const u64 base = (u64)s_memory;
|
||||
const u8* bits = s_unwind_info;
|
||||
|
||||
s_unwind.clear();
|
||||
s_unwind.reserve(m_map.size());
|
||||
std::vector<RUNTIME_FUNCTION> unwind;
|
||||
unwind.reserve(m_map.size());
|
||||
|
||||
for (const u64 addr : func_set)
|
||||
{
|
||||
@@ -304,7 +367,7 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
|
||||
uw.BeginAddress = static_cast<u32>(addr - base);
|
||||
uw.EndAddress = static_cast<u32>(next - base);
|
||||
uw.UnwindData = static_cast<u32>((u64)bits - base);
|
||||
s_unwind.emplace_back(uw);
|
||||
unwind.emplace_back(uw);
|
||||
|
||||
// Parse .xdata UNWIND_INFO record
|
||||
const u8 flags = *bits++; // Version and flags
|
||||
@@ -327,14 +390,16 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
|
||||
{
|
||||
LOG_ERROR(GENERAL, "LLVM: .xdata analysis failed! (%p != %p)", s_unwind_info + s_unwind_size, bits);
|
||||
}
|
||||
else if (!RtlAddFunctionTable(s_unwind.data(), (DWORD)s_unwind.size(), base))
|
||||
else if (!RtlAddFunctionTable(unwind.data(), (DWORD)unwind.size(), base))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "RtlAddFunctionTable(%p) failed! Error %u", s_unwind_info, GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_SUCCESS(GENERAL, "LLVM: UNWIND_INFO registered (%p, size=0x%llx)", s_unwind_info, s_unwind_size);
|
||||
LOG_NOTICE(GENERAL, "LLVM: UNWIND_INFO registered (%p, size=0x%llx)", s_unwind_info, s_unwind_size);
|
||||
}
|
||||
|
||||
s_unwind.emplace_back(std::move(unwind));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+24
-1
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#include "restore_new.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#endif
|
||||
@@ -17,6 +18,7 @@
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
#include "define_new_memleakdetect.h"
|
||||
|
||||
extern llvm::LLVMContext g_llvm_ctx;
|
||||
|
||||
@@ -29,10 +31,25 @@ class jit_compiler final
|
||||
// Compiled functions
|
||||
std::unordered_map<std::string, std::uintptr_t> m_map;
|
||||
|
||||
// Linkage cache
|
||||
std::unordered_map<std::string, std::uintptr_t> m_link;
|
||||
|
||||
// Arch
|
||||
std::string m_cpu;
|
||||
|
||||
// Internal
|
||||
void init();
|
||||
|
||||
public:
|
||||
jit_compiler(std::unique_ptr<llvm::Module>&&, std::unordered_map<std::string, std::uintptr_t>&&);
|
||||
jit_compiler(std::unordered_map<std::string, std::uintptr_t>);
|
||||
~jit_compiler();
|
||||
|
||||
// Compile module
|
||||
void make(std::unique_ptr<llvm::Module>, std::string);
|
||||
|
||||
// Load object
|
||||
void load(std::unique_ptr<llvm::Module>, std::unique_ptr<llvm::object::ObjectFile>);
|
||||
|
||||
// Get compiled function address
|
||||
std::uintptr_t get(const std::string& name) const
|
||||
{
|
||||
@@ -45,6 +62,12 @@ public:
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get CPU info
|
||||
const std::string& cpu() const
|
||||
{
|
||||
return m_cpu;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+55
-4
@@ -5,6 +5,12 @@
|
||||
#include "rpcs3_version.h"
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <chrono>
|
||||
#endif
|
||||
|
||||
// Thread-specific log prefix provider
|
||||
thread_local std::string(*g_tls_log_prefix)() = nullptr;
|
||||
|
||||
@@ -56,7 +62,7 @@ namespace logs
|
||||
}
|
||||
|
||||
// Encode level, current thread name, channel name and write log message
|
||||
virtual void log(const message& msg, const std::string& prefix, const std::string& text) override;
|
||||
virtual void log(u64 stamp, const message& msg, const std::string& prefix, const std::string& text) override;
|
||||
};
|
||||
|
||||
static file_listener* get_logger()
|
||||
@@ -66,6 +72,39 @@ namespace logs
|
||||
return &logger;
|
||||
}
|
||||
|
||||
static u64 get_stamp()
|
||||
{
|
||||
static struct time_initializer
|
||||
{
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER freq;
|
||||
LARGE_INTEGER start;
|
||||
|
||||
time_initializer()
|
||||
{
|
||||
QueryPerformanceFrequency(&freq);
|
||||
QueryPerformanceCounter(&start);
|
||||
}
|
||||
#else
|
||||
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
|
||||
#endif
|
||||
|
||||
u64 get() const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER now;
|
||||
QueryPerformanceCounter(&now);
|
||||
const LONGLONG diff = now.QuadPart - start.QuadPart;
|
||||
return diff / freq.QuadPart * 1'000'000 + diff % freq.QuadPart * 1'000'000 / freq.QuadPart;
|
||||
#else
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count();
|
||||
#endif
|
||||
}
|
||||
} timebase{};
|
||||
|
||||
return timebase.get();
|
||||
}
|
||||
|
||||
channel GENERAL(nullptr, level::notice);
|
||||
channel LOADER("LDR", level::notice);
|
||||
channel MEMORY("MEM", level::notice);
|
||||
@@ -76,6 +115,10 @@ namespace logs
|
||||
channel ARMv7("ARMv7");
|
||||
}
|
||||
|
||||
logs::listener::~listener()
|
||||
{
|
||||
}
|
||||
|
||||
void logs::listener::add(logs::listener* _new)
|
||||
{
|
||||
// Get first (main) listener
|
||||
@@ -90,6 +133,9 @@ void logs::listener::add(logs::listener* _new)
|
||||
|
||||
void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u64* args)
|
||||
{
|
||||
// Get timestamp
|
||||
const u64 stamp = get_stamp();
|
||||
|
||||
std::string text; fmt::raw_append(text, fmt, sup, args);
|
||||
std::string prefix(g_tls_log_prefix ? g_tls_log_prefix() : "");
|
||||
|
||||
@@ -99,7 +145,7 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u
|
||||
// Send message to all listeners
|
||||
while (lis)
|
||||
{
|
||||
lis->log(*this, prefix, text);
|
||||
lis->log(stamp, *this, prefix, text);
|
||||
lis = lis->m_next;
|
||||
}
|
||||
}
|
||||
@@ -126,7 +172,7 @@ void logs::file_writer::log(const char* text, std::size_t size)
|
||||
m_file.write(text, size);
|
||||
}
|
||||
|
||||
void logs::file_listener::log(const logs::message& msg, const std::string& prefix, const std::string& _text)
|
||||
void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::string& prefix, const std::string& _text)
|
||||
{
|
||||
std::string text; text.reserve(prefix.size() + _text.size() + 200);
|
||||
|
||||
@@ -143,7 +189,12 @@ void logs::file_listener::log(const logs::message& msg, const std::string& prefi
|
||||
case level::trace: text = u8"·T "; break;
|
||||
}
|
||||
|
||||
// TODO: print time?
|
||||
// Print miscosecond timestamp
|
||||
const u64 hours = stamp / 3600'000'000;
|
||||
const u64 mins = (stamp % 3600'000'000) / 60'000'000;
|
||||
const u64 secs = (stamp % 60'000'000) / 1'000'000;
|
||||
const u64 frac = (stamp % 1'000'000);
|
||||
fmt::append(text, "%u:%02u:%02u.%06u ", hours, mins, secs, frac);
|
||||
|
||||
if (prefix.size() > 0)
|
||||
{
|
||||
|
||||
+2
-2
@@ -40,10 +40,10 @@ namespace logs
|
||||
public:
|
||||
constexpr listener() = default;
|
||||
|
||||
virtual ~listener() = default;
|
||||
virtual ~listener();
|
||||
|
||||
// Process log message
|
||||
virtual void log(const message& msg, const std::string& prefix, const std::string& text) = 0;
|
||||
virtual void log(u64 stamp, const message& msg, const std::string& prefix, const std::string& text) = 0;
|
||||
|
||||
// Add new listener
|
||||
static void add(listener*);
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
#include "Utilities/Semaphore.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
struct benaphore::internal
|
||||
{
|
||||
std::mutex mutex;
|
||||
|
||||
std::size_t acq_order{};
|
||||
std::size_t rel_order{};
|
||||
|
||||
std::condition_variable cond;
|
||||
};
|
||||
|
||||
void benaphore::wait_hard()
|
||||
{
|
||||
initialize_once();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_data->mutex);
|
||||
|
||||
// Notify non-zero waiter queue size
|
||||
if (m_value.exchange(-1) == 1)
|
||||
{
|
||||
// Return immediately (acquired)
|
||||
m_value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Remember the order
|
||||
const std::size_t order = ++m_data->acq_order;
|
||||
|
||||
// Wait for the appropriate rel_order (TODO)
|
||||
while (m_data->rel_order < order)
|
||||
{
|
||||
m_data->cond.wait(lock);
|
||||
}
|
||||
|
||||
if (order == m_data->acq_order && m_data->acq_order == m_data->rel_order)
|
||||
{
|
||||
// Cleaup
|
||||
m_data->acq_order = 0;
|
||||
m_data->rel_order = 0;
|
||||
m_value.compare_and_swap(-1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void benaphore::post_hard()
|
||||
{
|
||||
initialize_once();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_data->mutex);
|
||||
|
||||
if (m_value.compare_and_swap(0, 1) != -1)
|
||||
{
|
||||
// Do nothing (released)
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_data->acq_order == m_data->rel_order)
|
||||
{
|
||||
m_value = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Awake one thread
|
||||
m_data->rel_order += 1;
|
||||
|
||||
// Unlock and notify
|
||||
lock.unlock();
|
||||
m_data->cond.notify_one();
|
||||
}
|
||||
|
||||
void benaphore::initialize_once()
|
||||
{
|
||||
if (UNLIKELY(!m_data))
|
||||
{
|
||||
auto ptr = new benaphore::internal;
|
||||
|
||||
if (!m_data.compare_and_swap_test(nullptr, ptr))
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
benaphore::~benaphore()
|
||||
{
|
||||
delete m_data;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
|
||||
// Binary semaphore
|
||||
class benaphore
|
||||
{
|
||||
struct internal;
|
||||
|
||||
// Reserved value (-1) enforces *_hard() calls
|
||||
atomic_t<u32> m_value{};
|
||||
|
||||
atomic_t<internal*> m_data{};
|
||||
|
||||
void wait_hard();
|
||||
void post_hard();
|
||||
|
||||
public:
|
||||
constexpr benaphore() = default;
|
||||
|
||||
~benaphore();
|
||||
|
||||
// Initialize internal data
|
||||
void initialize_once();
|
||||
|
||||
void wait()
|
||||
{
|
||||
if (UNLIKELY(!m_value.compare_and_swap_test(1, 0)))
|
||||
{
|
||||
wait_hard();
|
||||
}
|
||||
}
|
||||
|
||||
bool try_wait()
|
||||
{
|
||||
return m_value.compare_and_swap_test(1, 0);
|
||||
}
|
||||
|
||||
void post()
|
||||
{
|
||||
if (UNLIKELY(!m_value.compare_and_swap_test(0, 1)))
|
||||
{
|
||||
post_hard();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,187 +0,0 @@
|
||||
#include "SharedMutex.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
struct shared_mutex::internal
|
||||
{
|
||||
std::mutex mutex;
|
||||
|
||||
std::size_t rq_size{}; // Reader queue size (threads waiting on m_rcv)
|
||||
std::size_t wq_size{}; // Writer queue size (threads waiting on m_wcv and m_ocv)
|
||||
|
||||
std::condition_variable rcv; // Reader queue
|
||||
std::condition_variable wcv; // Writer queue
|
||||
std::condition_variable ocv; // For current exclusive owner
|
||||
};
|
||||
|
||||
void shared_mutex::lock_shared_hard()
|
||||
{
|
||||
initialize_once();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_data->mutex);
|
||||
|
||||
// Validate
|
||||
if ((m_ctrl & SM_INVALID_BIT) != 0) throw std::runtime_error("shared_mutex::lock_shared(): Invalid bit");
|
||||
if ((m_ctrl & SM_READER_MASK) == 0) throw std::runtime_error("shared_mutex::lock_shared(): No readers");
|
||||
|
||||
// Notify non-zero reader queue size
|
||||
m_ctrl |= SM_WAITERS_BIT, m_data->rq_size++;
|
||||
|
||||
// Fix excess reader count
|
||||
if ((--m_ctrl & SM_READER_MASK) == 0 && m_data->wq_size)
|
||||
{
|
||||
// Notify exclusive owner
|
||||
m_data->ocv.notify_one();
|
||||
}
|
||||
|
||||
// Obtain the reader lock
|
||||
while (true)
|
||||
{
|
||||
const auto ctrl = m_ctrl.load();
|
||||
|
||||
// Check writers and reader limit
|
||||
if (m_data->wq_size || (ctrl & ~SM_WAITERS_BIT) >= SM_READER_MAX)
|
||||
{
|
||||
m_data->rcv.wait(lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_ctrl.compare_and_swap_test(ctrl, ctrl + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!--m_data->rq_size && !m_data->wq_size)
|
||||
{
|
||||
m_ctrl &= ~SM_WAITERS_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::unlock_shared_notify()
|
||||
{
|
||||
initialize_once();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_data->mutex);
|
||||
|
||||
if ((m_ctrl & SM_READER_MASK) == 0 && m_data->wq_size)
|
||||
{
|
||||
// Notify exclusive owner
|
||||
lock.unlock();
|
||||
m_data->ocv.notify_one();
|
||||
}
|
||||
else if (m_data->rq_size)
|
||||
{
|
||||
// Notify other readers
|
||||
lock.unlock();
|
||||
m_data->rcv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::lock_hard()
|
||||
{
|
||||
initialize_once();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_data->mutex);
|
||||
|
||||
// Validate
|
||||
if ((m_ctrl & SM_INVALID_BIT) != 0) throw std::runtime_error("shared_mutex::lock(): Invalid bit");
|
||||
|
||||
// Notify non-zero writer queue size
|
||||
m_ctrl |= SM_WAITERS_BIT, m_data->wq_size++;
|
||||
|
||||
// Obtain the writer lock
|
||||
while (true)
|
||||
{
|
||||
const auto ctrl = m_ctrl.load();
|
||||
|
||||
if (ctrl & SM_WRITER_LOCK)
|
||||
{
|
||||
m_data->wcv.wait(lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_ctrl.compare_and_swap_test(ctrl, ctrl | SM_WRITER_LOCK))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for remaining readers
|
||||
while ((m_ctrl & SM_READER_MASK) != 0)
|
||||
{
|
||||
m_data->ocv.wait(lock);
|
||||
}
|
||||
|
||||
if (!--m_data->wq_size && !m_data->rq_size)
|
||||
{
|
||||
m_ctrl &= ~SM_WAITERS_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::unlock_notify()
|
||||
{
|
||||
initialize_once();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_data->mutex);
|
||||
|
||||
if (m_data->wq_size)
|
||||
{
|
||||
// Notify next exclusive owner
|
||||
lock.unlock();
|
||||
m_data->wcv.notify_one();
|
||||
}
|
||||
else if (m_data->rq_size)
|
||||
{
|
||||
// Notify all readers
|
||||
lock.unlock();
|
||||
m_data->rcv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::lock_upgrade_hard()
|
||||
{
|
||||
unlock_shared();
|
||||
lock();
|
||||
}
|
||||
|
||||
void shared_mutex::lock_degrade_hard()
|
||||
{
|
||||
initialize_once();
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_data->mutex);
|
||||
|
||||
m_ctrl -= SM_WRITER_LOCK - 1;
|
||||
|
||||
if (m_data->rq_size)
|
||||
{
|
||||
// Notify all readers
|
||||
lock.unlock();
|
||||
m_data->rcv.notify_all();
|
||||
}
|
||||
else if (m_data->wq_size)
|
||||
{
|
||||
// Notify next exclusive owner
|
||||
lock.unlock();
|
||||
m_data->wcv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::initialize_once()
|
||||
{
|
||||
if (UNLIKELY(!m_data))
|
||||
{
|
||||
auto ptr = new shared_mutex::internal;
|
||||
|
||||
if (!m_data.compare_and_swap_test(nullptr, ptr))
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shared_mutex::~shared_mutex()
|
||||
{
|
||||
delete m_data;
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
|
||||
//! An attempt to create effective implementation of "shared mutex", lock-free in optimistic case.
|
||||
//! All locking and unlocking may be done by a single LOCK XADD or LOCK CMPXCHG instruction.
|
||||
//! MSVC implementation of std::shared_timed_mutex seems suboptimal.
|
||||
//! std::shared_mutex is not available until C++17.
|
||||
class shared_mutex final
|
||||
{
|
||||
enum : u32
|
||||
{
|
||||
SM_WRITER_LOCK = 1u << 31, // Exclusive lock flag, must be MSB
|
||||
SM_WAITERS_BIT = 1u << 30, // Flag set if m_wq_size or m_rq_size is non-zero
|
||||
SM_INVALID_BIT = 1u << 29, // Unreachable reader count bit (may be set by incorrect unlock_shared() call)
|
||||
|
||||
SM_READER_MASK = SM_WAITERS_BIT - 1, // Valid reader count bit mask
|
||||
SM_READER_MAX = 1u << 24, // Max reader count
|
||||
};
|
||||
|
||||
atomic_t<u32> m_ctrl{}; // Control variable: reader count | SM_* flags
|
||||
|
||||
struct internal;
|
||||
|
||||
atomic_t<internal*> m_data{}; // Internal data
|
||||
|
||||
void lock_shared_hard();
|
||||
void unlock_shared_notify();
|
||||
|
||||
void lock_hard();
|
||||
void unlock_notify();
|
||||
|
||||
void lock_upgrade_hard();
|
||||
void lock_degrade_hard();
|
||||
|
||||
public:
|
||||
constexpr shared_mutex() = default;
|
||||
|
||||
// Initialize internal data
|
||||
void initialize_once();
|
||||
|
||||
~shared_mutex();
|
||||
|
||||
bool try_lock_shared()
|
||||
{
|
||||
const u32 ctrl = m_ctrl.load();
|
||||
|
||||
return ctrl < SM_READER_MAX && m_ctrl.compare_and_swap_test(ctrl, ctrl + 1);
|
||||
}
|
||||
|
||||
void lock_shared()
|
||||
{
|
||||
// Optimization: unconditional increment, compensated later
|
||||
if (UNLIKELY(m_ctrl++ >= SM_READER_MAX))
|
||||
{
|
||||
lock_shared_hard();
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_shared()
|
||||
{
|
||||
if (UNLIKELY(m_ctrl-- >= SM_READER_MAX))
|
||||
{
|
||||
unlock_shared_notify();
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
return !m_ctrl && m_ctrl.compare_and_swap_test(0, SM_WRITER_LOCK);
|
||||
}
|
||||
|
||||
void lock()
|
||||
{
|
||||
if (UNLIKELY(!m_ctrl.compare_and_swap_test(0, SM_WRITER_LOCK)))
|
||||
{
|
||||
lock_hard();
|
||||
}
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_ctrl &= ~SM_WRITER_LOCK;
|
||||
|
||||
if (UNLIKELY(m_ctrl))
|
||||
{
|
||||
unlock_notify();
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_upgrade()
|
||||
{
|
||||
return m_ctrl == 1 && m_ctrl.compare_and_swap_test(1, SM_WRITER_LOCK);
|
||||
}
|
||||
|
||||
bool try_lock_degrade()
|
||||
{
|
||||
return m_ctrl == SM_WRITER_LOCK && m_ctrl.compare_and_swap_test(SM_WRITER_LOCK, 1);
|
||||
}
|
||||
|
||||
void lock_upgrade()
|
||||
{
|
||||
if (UNLIKELY(!m_ctrl.compare_and_swap_test(1, SM_WRITER_LOCK)))
|
||||
{
|
||||
lock_upgrade_hard();
|
||||
}
|
||||
}
|
||||
|
||||
void lock_degrade()
|
||||
{
|
||||
if (UNLIKELY(!m_ctrl.compare_and_swap_test(SM_WRITER_LOCK, 1)))
|
||||
{
|
||||
lock_degrade_hard();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//! Simplified shared (reader) lock implementation.
|
||||
//! std::shared_lock may be used instead if necessary.
|
||||
class reader_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
|
||||
public:
|
||||
reader_lock(const reader_lock&) = delete;
|
||||
|
||||
reader_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
m_mutex.lock_shared();
|
||||
}
|
||||
|
||||
~reader_lock()
|
||||
{
|
||||
m_mutex.unlock_shared();
|
||||
}
|
||||
};
|
||||
|
||||
//! Simplified exclusive (writer) lock implementation.
|
||||
//! std::lock_guard may or std::unique_lock be used instead if necessary.
|
||||
class writer_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
|
||||
public:
|
||||
writer_lock(const writer_lock&) = delete;
|
||||
|
||||
writer_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
~writer_lock()
|
||||
{
|
||||
m_mutex.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
// Exclusive (writer) lock in the scope of shared (reader) lock.
|
||||
class upgraded_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
|
||||
public:
|
||||
upgraded_lock(const writer_lock&) = delete;
|
||||
|
||||
upgraded_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
m_mutex.lock_upgrade();
|
||||
}
|
||||
|
||||
~upgraded_lock()
|
||||
{
|
||||
m_mutex.lock_degrade();
|
||||
}
|
||||
};
|
||||
@@ -1,81 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
|
||||
// Tag used in sleep_entry<> constructor
|
||||
static struct defer_sleep_tag {} constexpr defer_sleep{};
|
||||
|
||||
// Define sleep queue as std::deque with T* pointers, T - thread type
|
||||
template<typename T> using sleep_queue = std::deque<T*>;
|
||||
|
||||
// Automatic object handling a thread pointer (T*) in the sleep queue
|
||||
// Sleep queue is actually std::deque with pointers, be careful about the lifetime
|
||||
template<typename T>
|
||||
class sleep_entry final
|
||||
{
|
||||
sleep_queue<T>& m_queue;
|
||||
T& m_thread;
|
||||
|
||||
public:
|
||||
// Constructor; enter() not called
|
||||
sleep_entry(sleep_queue<T>& queue, T& entry, const defer_sleep_tag&)
|
||||
: m_queue(queue)
|
||||
, m_thread(entry)
|
||||
{
|
||||
}
|
||||
|
||||
// Constructor; calls enter()
|
||||
sleep_entry(sleep_queue<T>& queue, T& entry)
|
||||
: sleep_entry(queue, entry, defer_sleep)
|
||||
{
|
||||
enter();
|
||||
}
|
||||
|
||||
// Destructor; calls leave()
|
||||
~sleep_entry()
|
||||
{
|
||||
leave();
|
||||
}
|
||||
|
||||
// Add thread to the sleep queue
|
||||
void enter()
|
||||
{
|
||||
for (auto t : m_queue)
|
||||
{
|
||||
if (t == &m_thread)
|
||||
{
|
||||
// Already exists, is it an error?
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_queue.emplace_back(&m_thread);
|
||||
}
|
||||
|
||||
// Remove thread from the sleep queue
|
||||
void leave()
|
||||
{
|
||||
for (auto it = m_queue.begin(), end = m_queue.end(); it != end; it++)
|
||||
{
|
||||
if (*it == &m_thread)
|
||||
{
|
||||
m_queue.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the thread exists in the sleep queue
|
||||
explicit operator bool() const
|
||||
{
|
||||
for (auto it = m_queue.begin(), end = m_queue.end(); it != end; it++)
|
||||
{
|
||||
if (*it == &m_thread)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
+15
-3
@@ -11,6 +11,18 @@
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
template <>
|
||||
void fmt_class_string<std::pair<const fmt_type_info*, u64>>::format(std::string& out, u64 arg)
|
||||
{
|
||||
// Dynamic format arg
|
||||
const auto& pair = get_object(arg);
|
||||
|
||||
if (pair.first)
|
||||
{
|
||||
pair.first->fmt_string(out, pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_class_string<const void*>::format(std::string& out, u64 arg)
|
||||
{
|
||||
if (arg)
|
||||
@@ -154,12 +166,12 @@ namespace fmt
|
||||
#ifdef _WIN32
|
||||
if (DWORD error = GetLastError())
|
||||
{
|
||||
fmt::append(out, " (e%#x)", error);
|
||||
fmt::append(out, " (e=%#x)", error);
|
||||
}
|
||||
#else
|
||||
if (int error = errno)
|
||||
{
|
||||
fmt::append(out, " (e%d)", error);
|
||||
fmt::append(out, " (e=%d)", error);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -240,7 +252,7 @@ struct fmt::cfmt_src
|
||||
template <typename T>
|
||||
T get(std::size_t index) const
|
||||
{
|
||||
return reinterpret_cast<const T&>(args[index]);
|
||||
return *reinterpret_cast<const T*>(reinterpret_cast<const u8*>(args + index));
|
||||
}
|
||||
|
||||
void skip(std::size_t extra)
|
||||
|
||||
+9
-4
@@ -60,9 +60,9 @@ struct fmt_unveil<T, std::enable_if_t<std::is_floating_point<T>::value && sizeof
|
||||
using type = T;
|
||||
|
||||
// Convert FP to f64 and reinterpret (TODO?)
|
||||
static inline u64 get(f64 arg)
|
||||
static inline u64 get(const f64 arg)
|
||||
{
|
||||
return reinterpret_cast<u64&>(arg);
|
||||
return *reinterpret_cast<const u64*>(reinterpret_cast<const u8*>(&arg));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -153,7 +153,7 @@ struct fmt_class_string
|
||||
|
||||
out += prefix;
|
||||
|
||||
for (u64 i = 0; i < 64; i++)
|
||||
for (u64 i = 0; i < 63; i++)
|
||||
{
|
||||
const u64 mask = 1ull << i;
|
||||
|
||||
@@ -161,13 +161,18 @@ struct fmt_class_string
|
||||
{
|
||||
fmt(out, i);
|
||||
|
||||
if (arg > mask)
|
||||
if (arg >> (i + 1))
|
||||
{
|
||||
out += delim;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (arg & (1ull << 63))
|
||||
{
|
||||
fmt(out, 63);
|
||||
}
|
||||
|
||||
out += suffix;
|
||||
}
|
||||
|
||||
|
||||
+229
-839
File diff suppressed because it is too large
Load Diff
+79
-267
@@ -7,6 +7,9 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "sema.h"
|
||||
#include "cond.h"
|
||||
|
||||
// Will report exception and call std::abort() if put in catch(...)
|
||||
[[noreturn]] void catch_all_exceptions();
|
||||
|
||||
@@ -17,19 +20,19 @@ class task_stack
|
||||
{
|
||||
std::unique_ptr<task_base> next;
|
||||
|
||||
virtual ~task_base() = default;
|
||||
virtual ~task_base();
|
||||
|
||||
virtual void exec()
|
||||
virtual void invoke()
|
||||
{
|
||||
if (next)
|
||||
{
|
||||
next->exec();
|
||||
next->invoke();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
struct task_type : task_base
|
||||
template <typename F>
|
||||
struct task_type final : task_base
|
||||
{
|
||||
std::remove_reference_t<F> func;
|
||||
|
||||
@@ -38,10 +41,10 @@ class task_stack
|
||||
{
|
||||
}
|
||||
|
||||
void exec() override
|
||||
void invoke() final override
|
||||
{
|
||||
func();
|
||||
task_base::exec();
|
||||
task_base::invoke();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -50,7 +53,7 @@ class task_stack
|
||||
public:
|
||||
task_stack() = default;
|
||||
|
||||
template<typename F>
|
||||
template <typename F>
|
||||
task_stack(F&& func)
|
||||
: m_stack(new task_type<F>(std::forward<F>(func)))
|
||||
{
|
||||
@@ -70,11 +73,11 @@ public:
|
||||
m_stack.reset();
|
||||
}
|
||||
|
||||
void exec() const
|
||||
void invoke() const
|
||||
{
|
||||
if (m_stack)
|
||||
{
|
||||
m_stack->exec();
|
||||
m_stack->invoke();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -82,23 +85,32 @@ public:
|
||||
// Thread control class
|
||||
class thread_ctrl final
|
||||
{
|
||||
public: // TODO
|
||||
struct internal;
|
||||
|
||||
private:
|
||||
// Current thread
|
||||
static thread_local thread_ctrl* g_tls_this_thread;
|
||||
|
||||
// Thread handle storage
|
||||
std::aligned_storage_t<16> m_thread;
|
||||
// Self pointer
|
||||
std::shared_ptr<thread_ctrl> m_self;
|
||||
|
||||
// Thread join contention counter
|
||||
atomic_t<u32> m_joining{};
|
||||
// Thread handle (platform-specific)
|
||||
atomic_t<std::uintptr_t> m_thread{0};
|
||||
|
||||
// Thread interrupt guard counter
|
||||
volatile u32 m_guard = 0x80000000;
|
||||
// Thread mutex
|
||||
mutable semaphore<> m_mutex;
|
||||
|
||||
// Thread internals
|
||||
atomic_t<internal*> m_data{};
|
||||
// Thread condition variable
|
||||
cond_variable m_cond;
|
||||
|
||||
// Thread flags
|
||||
atomic_t<u32> m_signal{0};
|
||||
|
||||
// Thread joining condition variable
|
||||
cond_variable m_jcv;
|
||||
|
||||
// Remotely set or caught exception
|
||||
std::exception_ptr m_exception;
|
||||
|
||||
// Thread initial task or atexit task
|
||||
task_stack m_task;
|
||||
|
||||
// Fixed name
|
||||
std::string m_name;
|
||||
@@ -110,19 +122,19 @@ private:
|
||||
void initialize();
|
||||
|
||||
// Called at the thread end
|
||||
void finalize() noexcept;
|
||||
void finalize(std::exception_ptr) noexcept;
|
||||
|
||||
// Get atexit function
|
||||
void push_atexit(task_stack);
|
||||
// Add task (atexit)
|
||||
static void _push(task_stack);
|
||||
|
||||
// Start waiting
|
||||
void wait_start(u64 timeout);
|
||||
// Internal waiting function, may throw. Infinite value is -1.
|
||||
static bool _wait_for(u64 usec);
|
||||
|
||||
// Proceed waiting
|
||||
bool wait_wait(u64 timeout);
|
||||
// Internal throwing function. Mutex must be locked and will be unlocked.
|
||||
[[noreturn]] void _throw();
|
||||
|
||||
// Check exception
|
||||
void test();
|
||||
// Internal notification function
|
||||
void _notify(cond_variable thread_ctrl::*);
|
||||
|
||||
public:
|
||||
thread_ctrl(std::string&& name);
|
||||
@@ -137,179 +149,57 @@ public:
|
||||
return m_name;
|
||||
}
|
||||
|
||||
// Initialize internal data
|
||||
void initialize_once();
|
||||
// Get exception
|
||||
std::exception_ptr get_exception() const;
|
||||
|
||||
// Set exception
|
||||
void set_exception(std::exception_ptr ptr);
|
||||
|
||||
// Get thread result (may throw, simultaneous joining allowed)
|
||||
void join();
|
||||
|
||||
// Lock thread mutex
|
||||
void lock();
|
||||
|
||||
// Lock conditionally (double-checked)
|
||||
template<typename F>
|
||||
bool lock_if(F&& pred)
|
||||
{
|
||||
if (pred())
|
||||
{
|
||||
lock();
|
||||
|
||||
try
|
||||
{
|
||||
if (LIKELY(pred()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
unlock();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock thread mutex (internal data must be initialized)
|
||||
void unlock();
|
||||
|
||||
// Lock, unlock, notify the thread (required if the condition changed locklessly)
|
||||
void lock_notify();
|
||||
|
||||
// Notify the thread (internal data must be initialized)
|
||||
// Notify the thread
|
||||
void notify();
|
||||
|
||||
// Set exception (internal data must be initialized, thread mutex must be locked)
|
||||
void set_exception(std::exception_ptr);
|
||||
|
||||
// Internal
|
||||
static void handle_interrupt();
|
||||
|
||||
// Interrupt thread with specified handler call (thread mutex must be locked)
|
||||
void interrupt(void(*handler)());
|
||||
|
||||
// Interrupt guard recursive enter
|
||||
void guard_enter()
|
||||
// Wait once with timeout. Abortable, may throw. May spuriously return false.
|
||||
static inline bool wait_for(u64 usec)
|
||||
{
|
||||
m_guard++;
|
||||
return _wait_for(usec);
|
||||
}
|
||||
|
||||
// Interrupt guard recursive leave
|
||||
void guard_leave()
|
||||
{
|
||||
if (UNLIKELY(--m_guard & 0x40000000))
|
||||
{
|
||||
test_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
// Allow interrupts
|
||||
void interrupt_enable()
|
||||
{
|
||||
m_guard &= ~0x80000000;
|
||||
}
|
||||
|
||||
// Disable and discard any interrupt
|
||||
void interrupt_disable()
|
||||
{
|
||||
m_guard |= 0x80000000;
|
||||
}
|
||||
|
||||
// Check interrupt if delayed by guard scope
|
||||
void test_interrupt();
|
||||
|
||||
// Current thread sleeps for specified amount of microseconds.
|
||||
// Wrapper for std::this_thread::sleep, doesn't require valid thread_ctrl.
|
||||
[[deprecated]] static void sleep(u64 useconds);
|
||||
|
||||
// Wait until pred(). Abortable, may throw. Thread must be locked.
|
||||
// Timeout in microseconds (zero means infinite).
|
||||
template<typename F>
|
||||
static inline auto wait_for(u64 useconds, F&& pred)
|
||||
{
|
||||
if (useconds)
|
||||
{
|
||||
g_tls_this_thread->wait_start(useconds);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
g_tls_this_thread->test();
|
||||
|
||||
if (auto&& result = pred())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else if (!g_tls_this_thread->wait_wait(useconds) && useconds)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait once. Abortable, may throw. Thread must be locked.
|
||||
// Timeout in microseconds (zero means infinite).
|
||||
static inline bool wait_for(u64 useconds = 0)
|
||||
{
|
||||
if (useconds)
|
||||
{
|
||||
g_tls_this_thread->wait_start(useconds);
|
||||
}
|
||||
|
||||
g_tls_this_thread->test();
|
||||
|
||||
if (!g_tls_this_thread->wait_wait(useconds) && useconds)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
g_tls_this_thread->test();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Wait until pred(). Abortable, may throw. Thread must be locked.
|
||||
template<typename F>
|
||||
static inline auto wait(F&& pred)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
g_tls_this_thread->test();
|
||||
|
||||
if (auto&& result = pred())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
g_tls_this_thread->wait_wait(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait once. Abortable, may throw. Thread must be locked.
|
||||
// Wait. Abortable, may throw.
|
||||
static inline void wait()
|
||||
{
|
||||
g_tls_this_thread->test();
|
||||
g_tls_this_thread->wait_wait(0);
|
||||
g_tls_this_thread->test();
|
||||
_wait_for(-1);
|
||||
}
|
||||
|
||||
// Wait eternally. Abortable, may throw. Thread must be locked.
|
||||
// Wait until pred(). Abortable, may throw.
|
||||
template<typename F, typename RT = std::result_of_t<F()>>
|
||||
static inline RT wait(F&& pred)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (RT result = pred())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
_wait_for(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait eternally until aborted.
|
||||
[[noreturn]] static inline void eternalize()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
g_tls_this_thread->test();
|
||||
g_tls_this_thread->wait_wait(0);
|
||||
_wait_for(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// Test exception (may throw).
|
||||
static void test();
|
||||
|
||||
// Get current thread (may be nullptr)
|
||||
static thread_ctrl* get_current()
|
||||
{
|
||||
@@ -320,14 +210,14 @@ public:
|
||||
template<typename F>
|
||||
static inline void atexit(F&& func)
|
||||
{
|
||||
return g_tls_this_thread->push_atexit(std::forward<F>(func));
|
||||
_push(std::forward<F>(func));
|
||||
}
|
||||
|
||||
// Named thread factory
|
||||
// Create detached named thread
|
||||
template<typename N, typename F>
|
||||
static inline void spawn(N&& name, F&& func)
|
||||
{
|
||||
auto&& out = std::make_shared<thread_ctrl>(std::forward<N>(name));
|
||||
auto out = std::make_shared<thread_ctrl>(std::forward<N>(name));
|
||||
|
||||
thread_ctrl::start(out, std::forward<F>(func));
|
||||
}
|
||||
@@ -382,7 +272,7 @@ public:
|
||||
}
|
||||
|
||||
// Access thread_ctrl
|
||||
thread_ctrl* operator->() const
|
||||
thread_ctrl* get() const
|
||||
{
|
||||
return m_thread.get();
|
||||
}
|
||||
@@ -392,90 +282,12 @@ public:
|
||||
return m_thread->join();
|
||||
}
|
||||
|
||||
void lock() const
|
||||
{
|
||||
return m_thread->lock();
|
||||
}
|
||||
|
||||
void unlock() const
|
||||
{
|
||||
return m_thread->unlock();
|
||||
}
|
||||
|
||||
void lock_notify() const
|
||||
{
|
||||
return m_thread->lock_notify();
|
||||
}
|
||||
|
||||
void notify() const
|
||||
{
|
||||
return m_thread->notify();
|
||||
}
|
||||
};
|
||||
|
||||
// Simple thread mutex locker
|
||||
class thread_lock final
|
||||
{
|
||||
thread_ctrl* m_thread;
|
||||
|
||||
public:
|
||||
thread_lock(const thread_lock&) = delete;
|
||||
|
||||
// Lock specified thread
|
||||
thread_lock(thread_ctrl* thread)
|
||||
: m_thread(thread)
|
||||
{
|
||||
m_thread->lock();
|
||||
}
|
||||
|
||||
// Lock specified named_thread
|
||||
thread_lock(named_thread& thread)
|
||||
: thread_lock(thread.operator->())
|
||||
{
|
||||
}
|
||||
|
||||
// Lock current thread
|
||||
thread_lock()
|
||||
: thread_lock(thread_ctrl::get_current())
|
||||
{
|
||||
}
|
||||
|
||||
~thread_lock()
|
||||
{
|
||||
m_thread->unlock();
|
||||
}
|
||||
};
|
||||
|
||||
// Interrupt guard scope
|
||||
class thread_guard final
|
||||
{
|
||||
thread_ctrl* m_thread;
|
||||
|
||||
public:
|
||||
thread_guard(const thread_guard&) = delete;
|
||||
|
||||
thread_guard(thread_ctrl* thread)
|
||||
: m_thread(thread)
|
||||
{
|
||||
m_thread->guard_enter();
|
||||
}
|
||||
|
||||
thread_guard(named_thread& thread)
|
||||
: thread_guard(thread.operator->())
|
||||
{
|
||||
}
|
||||
|
||||
thread_guard()
|
||||
: thread_guard(thread_ctrl::get_current())
|
||||
{
|
||||
}
|
||||
|
||||
~thread_guard() noexcept(false)
|
||||
{
|
||||
m_thread->guard_leave();
|
||||
}
|
||||
};
|
||||
|
||||
// Wrapper for named thread, joins automatically in the destructor, can only be used in function scope
|
||||
class scope_thread final
|
||||
{
|
||||
@@ -498,7 +310,7 @@ public:
|
||||
}
|
||||
|
||||
// Access thread_ctrl
|
||||
thread_ctrl* operator->() const
|
||||
thread_ctrl* get() const
|
||||
{
|
||||
return m_thread.get();
|
||||
}
|
||||
|
||||
+6
-6
@@ -6,8 +6,8 @@ class Timer
|
||||
{
|
||||
private:
|
||||
bool m_stopped;
|
||||
std::chrono::high_resolution_clock::time_point m_start;
|
||||
std::chrono::high_resolution_clock::time_point m_end;
|
||||
std::chrono::steady_clock::time_point m_start;
|
||||
std::chrono::steady_clock::time_point m_end;
|
||||
|
||||
public:
|
||||
Timer() : m_stopped(false)
|
||||
@@ -17,13 +17,13 @@ public:
|
||||
void Start()
|
||||
{
|
||||
m_stopped = false;
|
||||
m_start = std::chrono::high_resolution_clock::now();
|
||||
m_start = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
m_stopped = true;
|
||||
m_end = std::chrono::high_resolution_clock::now();
|
||||
m_end = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
double GetElapsedTimeInSec() const
|
||||
@@ -38,14 +38,14 @@ public:
|
||||
|
||||
u64 GetElapsedTimeInMicroSec() const
|
||||
{
|
||||
std::chrono::high_resolution_clock::time_point now = m_stopped ? m_end : std::chrono::high_resolution_clock::now();
|
||||
std::chrono::steady_clock::time_point now = m_stopped ? m_end : std::chrono::steady_clock::now();
|
||||
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(now - m_start).count();
|
||||
}
|
||||
|
||||
u64 GetElapsedTimeInNanoSec() const
|
||||
{
|
||||
std::chrono::high_resolution_clock::time_point now = m_stopped ? m_end : std::chrono::high_resolution_clock::now();
|
||||
std::chrono::steady_clock::time_point now = m_stopped ? m_end : std::chrono::steady_clock::now();
|
||||
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(now - m_start).count();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
namespace memory_helper
|
||||
{
|
||||
void* reserve_memory(size_t size)
|
||||
void* reserve_memory(std::size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return verify("reserve_memory" HERE, VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS));
|
||||
@@ -22,21 +22,21 @@ namespace memory_helper
|
||||
#endif
|
||||
}
|
||||
|
||||
void commit_page_memory(void* pointer, size_t size)
|
||||
void commit_page_memory(void* pointer, std::size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
verify(HERE), VirtualAlloc(pointer, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
#else
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), size, PROT_READ | PROT_WRITE) != -1;
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), PROT_READ | PROT_WRITE) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void free_reserved_memory(void* pointer, size_t size)
|
||||
void free_reserved_memory(void* pointer, std::size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
verify(HERE), VirtualFree(pointer, 0, MEM_DECOMMIT);
|
||||
#else
|
||||
verify(HERE), ::mprotect(pointer, size, PROT_NONE) != -1;
|
||||
verify(HERE), ::mmap(pointer, size, PROT_NONE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -581,7 +581,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
|
||||
const std::string _fmt(fmt - ctx.size, fmt);
|
||||
|
||||
const u64 arg0 = src.template get<u64>(0);
|
||||
const f64 arg0 = src.template get<f64>(0);
|
||||
const u64 arg1 = ctx.args >= 1 ? src.template get<u64>(1) : 0;
|
||||
const u64 arg2 = ctx.args >= 2 ? src.template get<u64>(2) : 0;
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "cond.h"
|
||||
#include "sync.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept
|
||||
{
|
||||
verify(HERE), _old != -1; // Very unlikely: it requires 2^32 distinct threads to wait simultaneously
|
||||
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER timeout;
|
||||
timeout.QuadPart = _timeout * -10;
|
||||
|
||||
if (HRESULT rc = NtWaitForKeyedEvent(nullptr, &m_value, false, _timeout == -1 ? nullptr : &timeout))
|
||||
{
|
||||
verify(HERE), rc == WAIT_TIMEOUT;
|
||||
|
||||
// Retire
|
||||
if (!m_value.fetch_op([](u32& value) { if (value) value--; }))
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
timespec timeout;
|
||||
timeout.tv_sec = _timeout / 1000000;
|
||||
timeout.tv_nsec = (_timeout % 1000000) * 1000;
|
||||
|
||||
for (u32 value = _old + 1;; value = m_value)
|
||||
{
|
||||
const int err = futex((int*)&m_value.raw(), FUTEX_WAIT_PRIVATE, value, _timeout == -1 ? nullptr : &timeout, nullptr, 0) == 0
|
||||
? 0
|
||||
: errno;
|
||||
|
||||
// Normal or timeout wakeup
|
||||
if (!err || (_timeout != -1 && err == ETIMEDOUT))
|
||||
{
|
||||
// Cleanup (remove waiter)
|
||||
verify(HERE), m_value--;
|
||||
return !err;
|
||||
}
|
||||
|
||||
// Not a wakeup
|
||||
verify(HERE), err == EAGAIN;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void cond_variable::imp_wake(u32 _count) noexcept
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Try to subtract required amount of waiters
|
||||
const u32 count = m_value.atomic_op([=](u32& value)
|
||||
{
|
||||
if (value > _count)
|
||||
{
|
||||
value -= _count;
|
||||
return _count;
|
||||
}
|
||||
|
||||
return std::exchange(value, 0);
|
||||
});
|
||||
|
||||
for (u32 i = count; i > 0; i--)
|
||||
{
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
#else
|
||||
for (u32 i = _count; i > 0; std::this_thread::yield())
|
||||
{
|
||||
const u32 value = m_value;
|
||||
|
||||
// Constrain remaining amount with imaginary waiter count
|
||||
if (i > value)
|
||||
{
|
||||
i = value;
|
||||
}
|
||||
|
||||
if (!value || i == 0)
|
||||
{
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
if (const int res = futex((int*)&m_value.raw(), FUTEX_WAKE_PRIVATE, i > INT_MAX ? INT_MAX : i, nullptr, nullptr, 0))
|
||||
{
|
||||
verify(HERE), res >= 0 && (u32)res <= i;
|
||||
i -= res;
|
||||
}
|
||||
|
||||
if (!m_value || i == 0)
|
||||
{
|
||||
// Escape
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
|
||||
// Lightweight condition variable
|
||||
class cond_variable
|
||||
{
|
||||
// Internal waiter counter
|
||||
atomic_t<u32> m_value{0};
|
||||
|
||||
protected:
|
||||
// Internal waiting function
|
||||
bool imp_wait(u32 _old, u64 _timeout) noexcept;
|
||||
|
||||
// Try to notify up to _count threads
|
||||
void imp_wake(u32 _count) noexcept;
|
||||
|
||||
public:
|
||||
constexpr cond_variable() = default;
|
||||
|
||||
// Intrusive wait algorithm for lockable objects
|
||||
template <typename T>
|
||||
explicit_bool_t wait(T& object, u64 usec_timeout = -1)
|
||||
{
|
||||
const u32 _old = m_value.fetch_add(1); // Increment waiter counter
|
||||
object.unlock();
|
||||
const bool res = imp_wait(_old, usec_timeout);
|
||||
object.lock();
|
||||
return res;
|
||||
}
|
||||
|
||||
// Wake one thread
|
||||
void notify_one() noexcept
|
||||
{
|
||||
if (m_value)
|
||||
{
|
||||
imp_wake(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Wake all threads
|
||||
void notify_all() noexcept
|
||||
{
|
||||
if (m_value)
|
||||
{
|
||||
imp_wake(-1);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -57,4 +57,13 @@ namespace utils
|
||||
{
|
||||
return loaded();
|
||||
}
|
||||
|
||||
void* get_proc_address(const char* lib, const char* name)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return reinterpret_cast<void*>(GetProcAddress(GetModuleHandleA(lib), name));
|
||||
#else
|
||||
return dlsym(dlopen(lib, RTLD_NOLOAD), name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,4 +38,43 @@ namespace utils
|
||||
bool loaded() const;
|
||||
explicit operator bool() const;
|
||||
};
|
||||
|
||||
// (assume the lib is always loaded)
|
||||
void* get_proc_address(const char* lib, const char* name);
|
||||
|
||||
template <typename F>
|
||||
struct dynamic_import
|
||||
{
|
||||
static_assert(sizeof(F) == 0, "Invalid function type");
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
struct dynamic_import<R(Args...)>
|
||||
{
|
||||
R(*ptr)(Args...);
|
||||
const char* const lib;
|
||||
const char* const name;
|
||||
|
||||
// Constant initialization
|
||||
constexpr dynamic_import(const char* lib, const char* name)
|
||||
: ptr(nullptr)
|
||||
, lib(lib)
|
||||
, name(name)
|
||||
{
|
||||
}
|
||||
|
||||
// Caller
|
||||
R operator()(Args... args)
|
||||
{
|
||||
if (!ptr)
|
||||
{
|
||||
// TODO: atomic
|
||||
ptr = reinterpret_cast<R(*)(Args...)>(get_proc_address(lib, name));
|
||||
}
|
||||
|
||||
return ptr(args...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#define DYNAMIC_IMPORT(lib, name, ...) static utils::dynamic_import<__VA_ARGS__> name(lib, #name);
|
||||
|
||||
@@ -1,8 +1,193 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
|
||||
#include "Atomic.h"
|
||||
|
||||
template <typename T, T Mod = T::__state_enum_max, typename Under = std::underlying_type_t<T>>
|
||||
T operator ++(T& value, int)
|
||||
{
|
||||
return std::exchange(value, static_cast<T>(value < T{} || value >= Mod ? static_cast<Under>(0) : static_cast<Under>(value) + 1));
|
||||
}
|
||||
|
||||
template <typename T, T Mod = T::__state_enum_max, typename Under = std::underlying_type_t<T>>
|
||||
T operator --(T& value, int)
|
||||
{
|
||||
return std::exchange(value, static_cast<T>(value <= T{} || value >= static_cast<Under>(Mod) - 1 ? static_cast<Under>(Mod) - 1 : static_cast<Under>(value) - 1));
|
||||
}
|
||||
|
||||
template <typename T, typename CRT, std::size_t Size = static_cast<std::underlying_type_t<T>>(T::__state_enum_max)>
|
||||
class state_machine
|
||||
{
|
||||
using under = std::underlying_type_t<T>;
|
||||
using ftype = void(CRT::*)(T);
|
||||
|
||||
atomic_t<T> m_value;
|
||||
|
||||
template <std::size_t... Ind>
|
||||
static inline ftype transition_map(std::integer_sequence<std::size_t, Ind...>, T state)
|
||||
{
|
||||
// Constantly initialized list of functions
|
||||
static constexpr ftype map[Size]{&CRT::template transition<static_cast<T>(Ind)>...};
|
||||
|
||||
// Unsafe table lookup (TODO)
|
||||
return map[static_cast<under>(state)];
|
||||
}
|
||||
|
||||
// "Convert" variable argument to template argument
|
||||
static inline ftype transition_get(T state)
|
||||
{
|
||||
return transition_map(std::make_index_sequence<Size>(), state);
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr state_machine()
|
||||
: m_value{T{}}
|
||||
{
|
||||
}
|
||||
|
||||
constexpr state_machine(T state)
|
||||
: m_value{state}
|
||||
{
|
||||
}
|
||||
|
||||
// Get current state
|
||||
T state_get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
// Unconditionally set state
|
||||
void state_set(T state)
|
||||
{
|
||||
T _old = m_value.exchange(state);
|
||||
|
||||
if (_old != state)
|
||||
{
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(_old);
|
||||
}
|
||||
}
|
||||
|
||||
// Conditionally set state (optimized)
|
||||
explicit_bool_t state_test_and_set(T expected, T state)
|
||||
{
|
||||
if (m_value == expected && m_value.compare_and_swap_test(expected, state))
|
||||
{
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(expected);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Conditionally set state (list version)
|
||||
explicit_bool_t state_test_and_set(std::initializer_list<T> expected, T state)
|
||||
{
|
||||
T _old;
|
||||
|
||||
if (m_value.atomic_op([&](T& value)
|
||||
{
|
||||
for (T x : expected)
|
||||
{
|
||||
if (value == x)
|
||||
{
|
||||
_old = std::exchange(value, state);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(_old);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unconditionally set next state
|
||||
void state_next()
|
||||
{
|
||||
T _old, state = m_value.op_fetch([&](T& value)
|
||||
{
|
||||
_old = value++;
|
||||
});
|
||||
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(_old);
|
||||
}
|
||||
|
||||
// Unconditionally set previous state
|
||||
void state_prev()
|
||||
{
|
||||
T _old, state = m_value.op_fetch([&](T& value)
|
||||
{
|
||||
_old = value--;
|
||||
});
|
||||
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(_old);
|
||||
}
|
||||
|
||||
// Get number of states
|
||||
static constexpr std::size_t size()
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
};
|
||||
|
||||
//enum class test_state
|
||||
//{
|
||||
// on,
|
||||
// off,
|
||||
// la,
|
||||
//
|
||||
// __state_enum_max // 3
|
||||
//};
|
||||
//
|
||||
//struct test_machine final : state_machine<test_state, test_machine>
|
||||
//{
|
||||
// template <test_state>
|
||||
// void transition(test_state old_state);
|
||||
//
|
||||
// void on()
|
||||
// {
|
||||
// state_set(test_state::on);
|
||||
// }
|
||||
//
|
||||
// void off()
|
||||
// {
|
||||
// state_set(test_state::off);
|
||||
// }
|
||||
//
|
||||
// void test()
|
||||
// {
|
||||
// state_next();
|
||||
// }
|
||||
//};
|
||||
//
|
||||
//template <>
|
||||
//void test_machine::transition<test_state::on>(test_state)
|
||||
//{
|
||||
// LOG_SUCCESS(GENERAL, "ON");
|
||||
//}
|
||||
//
|
||||
//template <>
|
||||
//void test_machine::transition<test_state::off>(test_state)
|
||||
//{
|
||||
// LOG_SUCCESS(GENERAL, "OFF");
|
||||
//}
|
||||
//
|
||||
//
|
||||
//template <>
|
||||
//void test_machine::transition<test_state::la>(test_state)
|
||||
//{
|
||||
// on();
|
||||
// off();
|
||||
// test();
|
||||
//}
|
||||
|
||||
enum class event_result
|
||||
{
|
||||
skip,
|
||||
|
||||
+186
-3
@@ -66,9 +66,9 @@ public:
|
||||
constexpr lf_fifo() = default;
|
||||
|
||||
// Get current "push" position
|
||||
u32 size()
|
||||
u32 size() const
|
||||
{
|
||||
return reinterpret_cast<atomic_t<u32>&>(m_ctrl).load(); // Hack
|
||||
return reinterpret_cast<const atomic_t<u32>&>(m_ctrl).load(); // Hack
|
||||
}
|
||||
|
||||
// Acquire the place for one or more elements.
|
||||
@@ -78,7 +78,7 @@ public:
|
||||
}
|
||||
|
||||
// Get current "pop" position
|
||||
u32 peek()
|
||||
u32 peek() const
|
||||
{
|
||||
return m_ctrl.load().pop;
|
||||
}
|
||||
@@ -145,3 +145,186 @@ public:
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Fixed-size single-producer single-consumer queue
|
||||
template <typename T, std::uint32_t N>
|
||||
class lf_spsc
|
||||
{
|
||||
// If N is a power of 2, m_push/m_pop can safely overflow and the algorithm is simplified
|
||||
static_assert(N && (1u << 31) % N == 0, "lf_spsc<> error: size must be power of 2");
|
||||
|
||||
protected:
|
||||
volatile std::uint32_t m_push{0};
|
||||
volatile std::uint32_t m_pop{0};
|
||||
T m_data[N]{};
|
||||
|
||||
public:
|
||||
constexpr lf_spsc() = default;
|
||||
|
||||
// Try to push (producer only)
|
||||
template <typename T2>
|
||||
bool try_push(T2&& data)
|
||||
{
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop >= N)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_mm_lfence();
|
||||
m_data[pos % N] = std::forward<T2>(data);
|
||||
_mm_sfence();
|
||||
m_push = pos + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try to get push pointer (producer only)
|
||||
operator T*()
|
||||
{
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop >= N)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_mm_lfence();
|
||||
return m_data + (pos % N);
|
||||
}
|
||||
|
||||
// Increment push counter (producer only)
|
||||
void end_push()
|
||||
{
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop < N)
|
||||
{
|
||||
_mm_sfence();
|
||||
m_push = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Unsafe access
|
||||
T& get_push(std::size_t i)
|
||||
{
|
||||
_mm_lfence();
|
||||
return m_data[(m_push + i) % N];
|
||||
}
|
||||
|
||||
// Try to pop (consumer only)
|
||||
template <typename T2>
|
||||
bool try_pop(T2& out)
|
||||
{
|
||||
const std::uint32_t pos = m_pop;
|
||||
|
||||
if (m_push - pos <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_mm_lfence();
|
||||
out = std::move(m_data[pos % N]);
|
||||
_mm_sfence();
|
||||
m_pop = pos + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Increment pop counter (consumer only)
|
||||
void end_pop()
|
||||
{
|
||||
const std::uint32_t pos = m_pop;
|
||||
|
||||
if (m_push - pos > 0)
|
||||
{
|
||||
_mm_sfence();
|
||||
m_pop = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Get size (consumer only)
|
||||
std::uint32_t size() const
|
||||
{
|
||||
return m_push - m_pop;
|
||||
}
|
||||
|
||||
// Direct access (consumer only)
|
||||
T& operator [](std::size_t i)
|
||||
{
|
||||
_mm_lfence();
|
||||
return m_data[(m_pop + i) % N];
|
||||
}
|
||||
};
|
||||
|
||||
// Fixed-size multi-producer single-consumer queue
|
||||
template <typename T, std::uint32_t N>
|
||||
class lf_mpsc : lf_spsc<T, N>
|
||||
{
|
||||
protected:
|
||||
using lf_spsc<T, N>::m_push;
|
||||
using lf_spsc<T, N>::m_pop;
|
||||
using lf_spsc<T, N>::m_data;
|
||||
|
||||
enum : std::uint64_t
|
||||
{
|
||||
c_ack = 1ull << 0,
|
||||
c_rel = 1ull << 32,
|
||||
};
|
||||
|
||||
atomic_t<std::uint64_t> m_lock{0};
|
||||
|
||||
void release(std::uint64_t value)
|
||||
{
|
||||
// Push all pending elements at once when possible
|
||||
if (value && value % c_rel == value / c_rel)
|
||||
{
|
||||
_mm_sfence();
|
||||
m_push += value % c_rel;
|
||||
m_lock.compare_and_swap_test(value, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr lf_mpsc() = default;
|
||||
|
||||
// Try to get push pointer
|
||||
operator T*()
|
||||
{
|
||||
const std::uint64_t old = m_lock.fetch_add(c_ack);
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (old % N >= N || pos - m_pop >= N - (old % N))
|
||||
{
|
||||
release(m_lock.sub_fetch(c_ack));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m_data + ((pos + old) % N);
|
||||
}
|
||||
|
||||
// Increment push counter (producer only)
|
||||
void end_push()
|
||||
{
|
||||
release(m_lock.add_fetch(c_rel));
|
||||
}
|
||||
|
||||
// Try to push
|
||||
template <typename T2>
|
||||
bool try_push(T2&& data)
|
||||
{
|
||||
if (T* ptr = *this)
|
||||
{
|
||||
*ptr = std::forward<T2>(data);
|
||||
end_push();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable consumer methods
|
||||
using lf_spsc<T, N>::try_pop;
|
||||
using lf_spsc<T, N>::end_pop;
|
||||
using lf_spsc<T, N>::size;
|
||||
using lf_spsc<T, N>::operator [];
|
||||
};
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
#include "mutex.h"
|
||||
#include "sync.h"
|
||||
|
||||
void shared_mutex::imp_lock_shared(s64 _old)
|
||||
{
|
||||
verify("shared_mutex overflow" HERE), _old <= c_max;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
const s64 value = m_value.load();
|
||||
|
||||
if (value >= c_min && m_value.compare_and_swap_test(value, value - c_min))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Acquire writer lock
|
||||
imp_wait(m_value.load());
|
||||
|
||||
// Convert value
|
||||
s64 value = m_value.fetch_add(c_one - c_min);
|
||||
|
||||
if (value != 0)
|
||||
{
|
||||
imp_unlock(value);
|
||||
}
|
||||
|
||||
// Wait as a reader if necessary
|
||||
if (value + c_one - c_min < 0)
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
|
||||
}
|
||||
#else
|
||||
while (true)
|
||||
{
|
||||
const s64 value0 = m_value.fetch_op([](s64& value)
|
||||
{
|
||||
if (value >= c_min)
|
||||
{
|
||||
value -= c_min;
|
||||
}
|
||||
});
|
||||
|
||||
if (value0 >= c_min)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Acquire writer lock
|
||||
imp_wait(value0);
|
||||
|
||||
// Convert to reader lock
|
||||
s64 value1 = m_value.fetch_add(c_one - c_min);
|
||||
|
||||
if (value1 != 0)
|
||||
{
|
||||
imp_unlock(value1);
|
||||
}
|
||||
|
||||
value1 += c_one - c_min;
|
||||
|
||||
if (value1 >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait as a reader if necessary
|
||||
while (futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAIT_PRIVATE, int(value1 >> 32), nullptr, nullptr, 0))
|
||||
{
|
||||
value1 = m_value.load();
|
||||
|
||||
if (value1 >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If blocked by writers, release the reader lock and try again
|
||||
const s64 value2 = m_value.fetch_op([](s64& value)
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
value += c_min;
|
||||
}
|
||||
});
|
||||
|
||||
if (value2 >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
imp_unlock_shared(value2);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock_shared(s64 _old)
|
||||
{
|
||||
verify("shared_mutex overflow" HERE), _old + c_min <= c_max;
|
||||
|
||||
// Check reader count, notify the writer if necessary
|
||||
if ((_old + c_min) % c_one == 0)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
#else
|
||||
m_value -= c_sig;
|
||||
|
||||
futex((int*)&m_value.raw() + IS_BE_MACHINE, FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::imp_wait(s64)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_value.sub_fetch(c_one))
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
#else
|
||||
if (!m_value.sub_fetch(c_one))
|
||||
{
|
||||
// Return immediately if locked
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Load new value, try to acquire c_sig
|
||||
const s64 value = m_value.fetch_op([](s64& value)
|
||||
{
|
||||
if (value <= c_one - c_sig)
|
||||
{
|
||||
value += c_sig;
|
||||
}
|
||||
});
|
||||
|
||||
if (value <= c_one - c_sig)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
futex((int*)&m_value.raw() + IS_BE_MACHINE, FUTEX_WAIT_PRIVATE, int(value), nullptr, nullptr, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock(s64 _old)
|
||||
{
|
||||
verify("shared_mutex overflow" HERE), _old <= c_max;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
const s64 value = m_value.load();
|
||||
|
||||
if (value == c_one && m_value.compare_and_swap_test(c_one, 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
imp_wait(m_value.load());
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock(s64 _old)
|
||||
{
|
||||
verify("shared_mutex overflow" HERE), _old + c_one <= c_max;
|
||||
|
||||
// 1) Notify the next writer if necessary
|
||||
// 2) Notify all readers otherwise if necessary
|
||||
#ifdef _WIN32
|
||||
if (_old + c_one <= 0)
|
||||
{
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
else if (s64 count = -_old / c_min)
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
NtReleaseKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (_old + c_one <= 0)
|
||||
{
|
||||
m_value -= c_sig;
|
||||
|
||||
futex((int*)&m_value.raw() + IS_BE_MACHINE, FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
|
||||
}
|
||||
else if (s64 count = -_old / c_min)
|
||||
{
|
||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_PRIVATE, INT_MAX, nullptr, nullptr, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock_upgrade()
|
||||
{
|
||||
unlock_shared();
|
||||
lock();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock_degrade()
|
||||
{
|
||||
unlock();
|
||||
lock_shared();
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock_shared()
|
||||
{
|
||||
// Conditional decrement
|
||||
return m_value.fetch_op([](s64& value) { if (value >= c_min) value -= c_min; }) >= c_min;
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock()
|
||||
{
|
||||
// Conditional decrement (TODO: obtain c_sig)
|
||||
return m_value.compare_and_swap_test(c_one, 0);
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock_upgrade()
|
||||
{
|
||||
// TODO
|
||||
return m_value.compare_and_swap_test(c_one - c_min, 0);
|
||||
}
|
||||
|
||||
bool shared_mutex::try_lock_degrade()
|
||||
{
|
||||
// TODO
|
||||
return m_value.compare_and_swap_test(0, c_one - c_min);
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
|
||||
// Shared mutex.
|
||||
class shared_mutex final
|
||||
{
|
||||
enum : s64
|
||||
{
|
||||
c_one = 1ull << 31, // Fixed-point 1.0 value (one writer)
|
||||
c_min = 0x00000001, // Fixed-point 1.0/max_readers value
|
||||
c_sig = 1ull << 62,
|
||||
c_max = c_one
|
||||
};
|
||||
|
||||
atomic_t<s64> m_value{c_one}; // Semaphore-alike counter
|
||||
|
||||
void imp_lock_shared(s64 _old);
|
||||
void imp_unlock_shared(s64 _old);
|
||||
void imp_wait(s64 _old);
|
||||
void imp_lock(s64 _old);
|
||||
void imp_unlock(s64 _old);
|
||||
|
||||
void imp_lock_upgrade();
|
||||
void imp_lock_degrade();
|
||||
|
||||
public:
|
||||
constexpr shared_mutex() = default;
|
||||
|
||||
bool try_lock_shared();
|
||||
|
||||
void lock_shared()
|
||||
{
|
||||
const s64 value = m_value.load();
|
||||
|
||||
// Fast path: decrement if positive
|
||||
if (UNLIKELY(value < c_min || value > c_one || !m_value.compare_and_swap_test(value, value - c_min)))
|
||||
{
|
||||
imp_lock_shared(value);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_shared()
|
||||
{
|
||||
// Unconditional increment
|
||||
const s64 value = m_value.fetch_add(c_min);
|
||||
|
||||
if (value < 0 || value > c_one - c_min)
|
||||
{
|
||||
imp_unlock_shared(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock();
|
||||
|
||||
void lock()
|
||||
{
|
||||
// Try to lock
|
||||
const s64 value = m_value.compare_and_swap(c_one, 0);
|
||||
|
||||
if (value != c_one)
|
||||
{
|
||||
imp_lock(value);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
// Unconditional increment
|
||||
const s64 value = m_value.fetch_add(c_one);
|
||||
|
||||
if (value != 0)
|
||||
{
|
||||
imp_unlock(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_upgrade();
|
||||
|
||||
void lock_upgrade()
|
||||
{
|
||||
if (!m_value.compare_and_swap_test(c_one - c_min, 0))
|
||||
{
|
||||
imp_lock_upgrade();
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_degrade();
|
||||
|
||||
void lock_degrade()
|
||||
{
|
||||
if (!m_value.compare_and_swap_test(0, c_one - c_min))
|
||||
{
|
||||
imp_lock_degrade();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Simplified shared (reader) lock implementation.
|
||||
class reader_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
bool m_upgraded = false;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m_upgraded ? m_mutex.lock() : m_mutex.lock_shared();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_upgraded ? m_mutex.unlock() : m_mutex.unlock_shared();
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
reader_lock(const reader_lock&) = delete;
|
||||
|
||||
explicit reader_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
lock();
|
||||
}
|
||||
|
||||
// One-way lock upgrade
|
||||
void upgrade()
|
||||
{
|
||||
if (!m_upgraded)
|
||||
{
|
||||
m_mutex.lock_upgrade();
|
||||
m_upgraded = true;
|
||||
}
|
||||
}
|
||||
|
||||
~reader_lock()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
};
|
||||
|
||||
// Simplified exclusive (writer) lock implementation.
|
||||
class writer_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
writer_lock(const writer_lock&) = delete;
|
||||
|
||||
explicit writer_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
lock();
|
||||
}
|
||||
|
||||
~writer_lock()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
};
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "stdafx.h"
|
||||
#include "restore_new.h"
|
||||
#include "Utilities/rXml.h"
|
||||
#include "define_new_memleakdetect.h"
|
||||
|
||||
rXmlNode::rXmlNode() : handle()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
#include "sema.h"
|
||||
#include "sync.h"
|
||||
|
||||
void semaphore_base::imp_wait()
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
const s32 value = m_value.load();
|
||||
|
||||
if (value > 0 && m_value.compare_and_swap_test(value, value - 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
const s32 value = m_value.fetch_sub(1);
|
||||
|
||||
if (value <= 0)
|
||||
{
|
||||
NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
#else
|
||||
while (true)
|
||||
{
|
||||
// Try hard way
|
||||
const s32 value = m_value.op_fetch([](s32& value)
|
||||
{
|
||||
// Use sign bit to acknowledge waiter presence
|
||||
if (value && value > INT32_MIN)
|
||||
{
|
||||
value--;
|
||||
|
||||
if (value < 0)
|
||||
{
|
||||
// Remove sign bit
|
||||
value -= INT32_MIN;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set sign bit
|
||||
value = INT32_MIN;
|
||||
}
|
||||
});
|
||||
|
||||
if (value >= 0)
|
||||
{
|
||||
// Signal other waiter to wake up or to restore sign bit
|
||||
futex(&m_value.raw(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
futex(&m_value.raw(), FUTEX_WAIT_PRIVATE, value, nullptr, nullptr, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void semaphore_base::imp_post(s32 _old)
|
||||
{
|
||||
verify("semaphore_base: overflow" HERE), _old < 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
#else
|
||||
futex(&m_value.raw(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool semaphore_base::try_wait()
|
||||
{
|
||||
// Conditional decrement
|
||||
const s32 value = m_value.fetch_op([](s32& value)
|
||||
{
|
||||
if (value > 0)
|
||||
{
|
||||
value -= 1;
|
||||
}
|
||||
});
|
||||
|
||||
return value > 0;
|
||||
}
|
||||
|
||||
bool semaphore_base::try_post(s32 _max)
|
||||
{
|
||||
// Conditional increment
|
||||
const s32 value = m_value.fetch_op([&](s32& value)
|
||||
{
|
||||
if (value < _max)
|
||||
{
|
||||
value += 1;
|
||||
}
|
||||
});
|
||||
|
||||
if (value < 0)
|
||||
{
|
||||
imp_post(value);
|
||||
}
|
||||
|
||||
return value < _max;
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
|
||||
// Lightweight semaphore helper class
|
||||
class semaphore_base
|
||||
{
|
||||
// Semaphore value
|
||||
atomic_t<s32> m_value;
|
||||
|
||||
void imp_wait();
|
||||
|
||||
void imp_post(s32 _old);
|
||||
|
||||
friend class semaphore_lock;
|
||||
|
||||
protected:
|
||||
explicit constexpr semaphore_base(s32 value)
|
||||
: m_value{value}
|
||||
{
|
||||
}
|
||||
|
||||
void wait()
|
||||
{
|
||||
// Load value
|
||||
const s32 value = m_value.load();
|
||||
|
||||
// Conditional decrement
|
||||
if (UNLIKELY(value <= 0 || !m_value.compare_and_swap_test(value, value - 1)))
|
||||
{
|
||||
imp_wait();
|
||||
}
|
||||
}
|
||||
|
||||
bool try_wait();
|
||||
|
||||
void post(s32 _max)
|
||||
{
|
||||
// Unconditional increment
|
||||
const s32 value = m_value.fetch_add(1);
|
||||
|
||||
if (UNLIKELY(value < 0 || value >= _max))
|
||||
{
|
||||
imp_post(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_post(s32 _max);
|
||||
|
||||
public:
|
||||
// Get current semaphore value
|
||||
s32 get() const
|
||||
{
|
||||
// Load value
|
||||
const s32 value = m_value;
|
||||
|
||||
// Return only positive value
|
||||
return value < 0 ? 0 : value;
|
||||
}
|
||||
};
|
||||
|
||||
// Lightweight semaphore template (default arguments define binary semaphore and Def == Max)
|
||||
template <s32 Max = 1, s32 Def = Max>
|
||||
class semaphore final : public semaphore_base
|
||||
{
|
||||
static_assert(Max >= 0, "semaphore<>: Max is out of bounds");
|
||||
static_assert(Def >= 0, "semaphore<>: Def is out of bounds");
|
||||
static_assert(Def <= Max, "semaphore<>: Def is too big");
|
||||
|
||||
using base = semaphore_base;
|
||||
|
||||
public:
|
||||
// Default constructor (recommended)
|
||||
constexpr semaphore()
|
||||
: base{Def}
|
||||
{
|
||||
}
|
||||
|
||||
// Explicit value constructor (not recommended)
|
||||
explicit constexpr semaphore(s32 value)
|
||||
: base{value}
|
||||
{
|
||||
}
|
||||
|
||||
// Obtain a semaphore
|
||||
void wait()
|
||||
{
|
||||
return base::wait();
|
||||
}
|
||||
|
||||
// Try to obtain a semaphore
|
||||
explicit_bool_t try_wait()
|
||||
{
|
||||
return base::try_wait();
|
||||
}
|
||||
|
||||
// Return a semaphore
|
||||
void post()
|
||||
{
|
||||
return base::post(Max);
|
||||
}
|
||||
|
||||
// Try to return a semaphore
|
||||
explicit_bool_t try_post()
|
||||
{
|
||||
return base::try_post(Max);
|
||||
}
|
||||
|
||||
// Get max semaphore value
|
||||
static constexpr s32 size()
|
||||
{
|
||||
return Max;
|
||||
}
|
||||
};
|
||||
|
||||
class semaphore_lock
|
||||
{
|
||||
semaphore_base& m_base;
|
||||
|
||||
void lock()
|
||||
{
|
||||
m_base.wait();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_base.post(INT32_MAX);
|
||||
}
|
||||
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
explicit semaphore_lock(const semaphore_lock&) = delete;
|
||||
|
||||
semaphore_lock(semaphore_base& sema)
|
||||
: m_base(sema)
|
||||
{
|
||||
lock();
|
||||
}
|
||||
|
||||
~semaphore_lock()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
};
|
||||
+135
-175
@@ -4,185 +4,145 @@
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
#include "dynamic_library.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#define DYNAMIC_IMPORT(handle, name) do { name = reinterpret_cast<decltype(name)>(GetProcAddress(handle, #name)); } while (0)
|
||||
|
||||
static NTSTATUS(*NtSetTimerResolution)(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution);
|
||||
static NTSTATUS(*NtWaitForKeyedEvent)(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout);
|
||||
static NTSTATUS(*NtReleaseKeyedEvent)(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout);
|
||||
|
||||
namespace util
|
||||
{
|
||||
static const bool keyed_init = []
|
||||
{
|
||||
const auto handle = LoadLibraryA("ntdll.dll");
|
||||
DYNAMIC_IMPORT(handle, NtSetTimerResolution);
|
||||
DYNAMIC_IMPORT(handle, NtWaitForKeyedEvent);
|
||||
DYNAMIC_IMPORT(handle, NtReleaseKeyedEvent);
|
||||
FreeLibrary(handle);
|
||||
|
||||
ULONG res = 100;
|
||||
NtSetTimerResolution(100, TRUE, &res);
|
||||
|
||||
return NtWaitForKeyedEvent && NtReleaseKeyedEvent;
|
||||
}();
|
||||
|
||||
// Wait for specified condition. func() acknowledges success by value modification.
|
||||
template<typename F>
|
||||
inline void keyed_wait(atomic_t<u32>& key, F&& func)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
u32 read = key.load();
|
||||
u32 copy = read;
|
||||
|
||||
while (func(read), read != copy)
|
||||
{
|
||||
read = key.compare_and_swap(copy, read);
|
||||
|
||||
if (copy == read)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
copy = read;
|
||||
}
|
||||
|
||||
NtWaitForKeyedEvent(NULL, &key, FALSE, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to wake up a thread.
|
||||
inline bool keyed_post(atomic_t<u32>& key, u32 acknowledged_value)
|
||||
{
|
||||
LARGE_INTEGER timeout;
|
||||
timeout.QuadPart = -50;
|
||||
|
||||
while (UNLIKELY(NtReleaseKeyedEvent(NULL, &key, FALSE, &timeout) != ERROR_SUCCESS))
|
||||
{
|
||||
if (key.load() != acknowledged_value)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct native_rwlock
|
||||
{
|
||||
SRWLOCK rwlock = SRWLOCK_INIT;
|
||||
|
||||
constexpr native_rwlock() = default;
|
||||
|
||||
native_rwlock(const native_rwlock&) = delete;
|
||||
|
||||
void lock()
|
||||
{
|
||||
AcquireSRWLockExclusive(&rwlock);
|
||||
}
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
return TryAcquireSRWLockExclusive(&rwlock) != 0;
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
ReleaseSRWLockExclusive(&rwlock);
|
||||
}
|
||||
|
||||
void lock_shared()
|
||||
{
|
||||
AcquireSRWLockShared(&rwlock);
|
||||
}
|
||||
|
||||
bool try_lock_shared()
|
||||
{
|
||||
return TryAcquireSRWLockShared(&rwlock) != 0;
|
||||
}
|
||||
|
||||
void unlock_shared()
|
||||
{
|
||||
ReleaseSRWLockShared(&rwlock);
|
||||
}
|
||||
};
|
||||
|
||||
struct native_cond
|
||||
{
|
||||
CONDITION_VARIABLE cond = CONDITION_VARIABLE_INIT;
|
||||
|
||||
constexpr native_cond() = default;
|
||||
|
||||
native_cond(const native_cond&) = delete;
|
||||
|
||||
void notify_one()
|
||||
{
|
||||
WakeConditionVariable(&cond);
|
||||
}
|
||||
|
||||
void notify_all()
|
||||
{
|
||||
WakeAllConditionVariable(&cond);
|
||||
}
|
||||
|
||||
void wait(native_rwlock& rwlock)
|
||||
{
|
||||
SleepConditionVariableSRW(&cond, &rwlock.rwlock, INFINITE, 0);
|
||||
}
|
||||
|
||||
void wait_shared(native_rwlock& rwlock)
|
||||
{
|
||||
SleepConditionVariableSRW(&cond, &rwlock.rwlock, INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED);
|
||||
}
|
||||
};
|
||||
|
||||
class exclusive_lock
|
||||
{
|
||||
native_rwlock& m_rwlock;
|
||||
|
||||
public:
|
||||
exclusive_lock(native_rwlock& rwlock)
|
||||
: m_rwlock(rwlock)
|
||||
{
|
||||
m_rwlock.lock();
|
||||
}
|
||||
|
||||
~exclusive_lock()
|
||||
{
|
||||
m_rwlock.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
class shared_lock
|
||||
{
|
||||
native_rwlock& m_rwlock;
|
||||
|
||||
public:
|
||||
shared_lock(native_rwlock& rwlock)
|
||||
: m_rwlock(rwlock)
|
||||
{
|
||||
m_rwlock.lock_shared();
|
||||
}
|
||||
|
||||
~shared_lock()
|
||||
{
|
||||
m_rwlock.unlock_shared();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#include <time.h>
|
||||
#elif __linux__
|
||||
#include <errno.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/futex.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#endif
|
||||
#include <ctime>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace util
|
||||
{
|
||||
struct native_rwlock;
|
||||
struct native_cond;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution));
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtWaitForKeyedEvent, NTSTATUS(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout));
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtReleaseKeyedEvent, NTSTATUS(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout));
|
||||
DYNAMIC_IMPORT("ntdll.dll", NtDelayExecution, NTSTATUS(BOOLEAN Alertable, PLARGE_INTEGER DelayInterval));
|
||||
#endif
|
||||
|
||||
CHECK_SIZE_ALIGN(util::native_rwlock, sizeof(void*), alignof(void*));
|
||||
CHECK_SIZE_ALIGN(util::native_cond, sizeof(void*), alignof(void*));
|
||||
#ifndef __linux__
|
||||
enum
|
||||
{
|
||||
FUTEX_PRIVATE_FLAG = 0,
|
||||
FUTEX_WAIT = 0,
|
||||
FUTEX_WAIT_PRIVATE = FUTEX_WAIT,
|
||||
FUTEX_WAKE = 1,
|
||||
FUTEX_WAKE_PRIVATE = FUTEX_WAKE,
|
||||
FUTEX_BITSET = 2,
|
||||
FUTEX_WAIT_BITSET = FUTEX_WAIT | FUTEX_BITSET,
|
||||
FUTEX_WAIT_BITSET_PRIVATE = FUTEX_WAIT_BITSET,
|
||||
FUTEX_WAKE_BITSET = FUTEX_WAKE | FUTEX_BITSET,
|
||||
FUTEX_WAKE_BITSET_PRIVATE = FUTEX_WAKE_BITSET,
|
||||
};
|
||||
#endif
|
||||
|
||||
inline int futex(int* uaddr, int futex_op, int val, const timespec* timeout, int* uaddr2, int val3)
|
||||
{
|
||||
#ifdef __linux__
|
||||
return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr, val3);
|
||||
#else
|
||||
static struct futex_map
|
||||
{
|
||||
struct waiter
|
||||
{
|
||||
int val;
|
||||
uint mask;
|
||||
std::condition_variable cv;
|
||||
};
|
||||
|
||||
std::mutex mutex;
|
||||
std::unordered_multimap<int*, waiter*, pointer_hash<int>> map;
|
||||
|
||||
int operator()(int* uaddr, int futex_op, int val, const timespec* timeout, int*, uint val3)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
||||
switch (futex_op)
|
||||
{
|
||||
case FUTEX_WAIT:
|
||||
{
|
||||
val3 = -1;
|
||||
// Fallthrough
|
||||
}
|
||||
case FUTEX_WAIT_BITSET:
|
||||
{
|
||||
if (*(volatile int*)uaddr != val)
|
||||
{
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
waiter rec;
|
||||
rec.val = val;
|
||||
rec.mask = val3;
|
||||
const auto& ref = *map.emplace(uaddr, &rec);
|
||||
|
||||
int res = 0;
|
||||
|
||||
if (!timeout)
|
||||
{
|
||||
rec.cv.wait(lock, [&] { return !rec.mask; });
|
||||
}
|
||||
else if (futex_op == FUTEX_WAIT)
|
||||
{
|
||||
const auto nsec = std::chrono::nanoseconds(timeout->tv_nsec + timeout->tv_sec * 1000000000ull);
|
||||
|
||||
if (!rec.cv.wait_for(lock, nsec, [&] { return !rec.mask; }))
|
||||
{
|
||||
res = -1;
|
||||
errno = ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
map.erase(std::find(map.find(uaddr), map.end(), ref));
|
||||
return res;
|
||||
}
|
||||
|
||||
case FUTEX_WAKE:
|
||||
{
|
||||
val3 = -1;
|
||||
// Fallthrough
|
||||
}
|
||||
case FUTEX_WAKE_BITSET:
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
for (auto range = map.equal_range(uaddr); val && range.first != range.second; range.first++)
|
||||
{
|
||||
auto& entry = *range.first->second;
|
||||
|
||||
if (entry.mask & val3)
|
||||
{
|
||||
entry.cv.notify_one();
|
||||
entry.mask = 0;
|
||||
res++;
|
||||
val--;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
} g_futex;
|
||||
|
||||
return g_futex(uaddr, futex_op, val, timeout, uaddr2, val3);
|
||||
#endif
|
||||
}
|
||||
|
||||
+55
-40
@@ -11,6 +11,7 @@
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <chrono>
|
||||
|
||||
// Assume little-endian
|
||||
#define IS_LE_MACHINE 1
|
||||
@@ -30,8 +31,6 @@
|
||||
#define SAFE_BUFFERS
|
||||
#define NEVER_INLINE __attribute__((noinline))
|
||||
#define FORCE_INLINE __attribute__((always_inline)) inline
|
||||
|
||||
// Some platforms don't support thread_local well yet.
|
||||
#define thread_local __thread
|
||||
#endif
|
||||
|
||||
@@ -39,7 +38,6 @@
|
||||
#define CHECK_ALIGN(type, align) static_assert(alignof(type) == align, "Invalid " #type " type alignment")
|
||||
#define CHECK_MAX_SIZE(type, size) static_assert(sizeof(type) <= size, #type " type size is too big")
|
||||
#define CHECK_SIZE_ALIGN(type, size, align) CHECK_SIZE(type, size); CHECK_ALIGN(type, align)
|
||||
#define CHECK_STORAGE(type, storage) static_assert(sizeof(type) <= sizeof(storage) && alignof(type) <= alignof(decltype(storage)), #type " is too small")
|
||||
|
||||
// Return 32 bit sizeof() to avoid widening/narrowing conversions with size_t
|
||||
#define SIZE_32(...) static_cast<u32>(sizeof(__VA_ARGS__))
|
||||
@@ -84,6 +82,10 @@ using s16 = std::int16_t;
|
||||
using s32 = std::int32_t;
|
||||
using s64 = std::int64_t;
|
||||
|
||||
using steady_clock = std::conditional<
|
||||
std::chrono::high_resolution_clock::is_steady,
|
||||
std::chrono::high_resolution_clock, std::chrono::steady_clock>::type;
|
||||
|
||||
namespace gsl
|
||||
{
|
||||
enum class byte : u8;
|
||||
@@ -533,7 +535,7 @@ struct verify_impl
|
||||
// Verification (can be safely disabled)
|
||||
if (!verify_func()(std::forward<T>(value)))
|
||||
{
|
||||
fmt::raw_verify_error(cause, fmt::get_type_info<uint>(), N);
|
||||
fmt::raw_verify_error(cause, nullptr, N);
|
||||
}
|
||||
|
||||
return verify_impl<N + 1>{cause};
|
||||
@@ -843,34 +845,6 @@ struct multicast<T, Value, void>
|
||||
}
|
||||
};
|
||||
|
||||
// Tagged ID type
|
||||
template <typename T = void, typename ID = u32>
|
||||
class id_value
|
||||
{
|
||||
// Initial value
|
||||
mutable ID m_value{static_cast<ID>(-1)};
|
||||
|
||||
// Allow access for ID manager
|
||||
friend class idm;
|
||||
|
||||
// Update ID
|
||||
void operator=(const ID& value) const
|
||||
{
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr id_value()
|
||||
{
|
||||
}
|
||||
|
||||
// Get the value
|
||||
operator ID() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
};
|
||||
|
||||
// Error code type (return type), implements error reporting. Could be a template.
|
||||
struct error_code
|
||||
{
|
||||
@@ -926,13 +900,54 @@ constexpr FORCE_INLINE error_code::not_an_error not_an_error(const T& value)
|
||||
return static_cast<error_code::not_an_error>(static_cast<s32>(value));
|
||||
}
|
||||
|
||||
template <typename T, typename ID>
|
||||
struct fmt_unveil<id_value<T, ID>>
|
||||
// Synchronization helper (cache-friendly busy waiting)
|
||||
inline void busy_wait(std::size_t count = 100)
|
||||
{
|
||||
using type = typename fmt_unveil<ID>::type;
|
||||
while (count--) _mm_pause();
|
||||
}
|
||||
|
||||
static inline auto get(const id_value<T, ID>& value)
|
||||
{
|
||||
return fmt_unveil<ID>::get(value);
|
||||
}
|
||||
};
|
||||
// Rotate helpers
|
||||
#if defined(__GNUG__)
|
||||
|
||||
inline u8 rol8(const u8 x, const u8 n)
|
||||
{
|
||||
u8 result = x;
|
||||
__asm__("rolb %[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u16 rol16(const u16 x, const u16 n)
|
||||
{
|
||||
u16 result = x;
|
||||
__asm__("rolw %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u32 rol32(const u32 x, const u32 n)
|
||||
{
|
||||
u32 result = x;
|
||||
__asm__("roll %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u64 rol64(const u64 x, const u64 n)
|
||||
{
|
||||
u64 result = x;
|
||||
__asm__("rolq %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u64 ror64(const u64 x, const u64 n)
|
||||
{
|
||||
u64 result = x;
|
||||
__asm__("rorq %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||
return result;
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
inline u8 rol8(const u8 x, const u8 n) { return _rotl8(x, n); }
|
||||
inline u16 rol16(const u16 x, const u16 n) { return _rotl16(x, (u8)n); }
|
||||
inline u32 rol32(const u32 x, const u32 n) { return _rotl(x, (int)n); }
|
||||
inline u64 rol64(const u64 x, const u64 n) { return _rotl64(x, (int)n); }
|
||||
inline u64 ror64(const u64 x, const u64 n) { return _rotr64(x, (int)n); }
|
||||
#endif
|
||||
|
||||
@@ -73,16 +73,6 @@ namespace std {
|
||||
|
||||
}; // class bad_variant_access
|
||||
|
||||
template <typename R = void>
|
||||
struct MAPBOX_VARIANT_DEPRECATED static_visitor
|
||||
{
|
||||
using result_type = R;
|
||||
|
||||
protected:
|
||||
static_visitor() {}
|
||||
~static_visitor() {}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
static constexpr std::size_t invalid_value = std::size_t(-1);
|
||||
|
||||
+11
-7
@@ -1,19 +1,22 @@
|
||||
version: 1.0.{build}
|
||||
version: '0.0.2-{build}'
|
||||
|
||||
os: Visual Studio 2015
|
||||
configuration:
|
||||
# - Release
|
||||
- ReleaseLLVM
|
||||
platform: x64
|
||||
clone_folder: c:\rpcs3
|
||||
clone_folder: C:\rpcs3
|
||||
clone_depth: 3
|
||||
test: off
|
||||
|
||||
configuration:
|
||||
# Release
|
||||
- ReleaseLLVM
|
||||
|
||||
before_build:
|
||||
- ps: $env:Date="$(git show -s --date=short --format='%ad')"
|
||||
- git submodule update --init 3rdparty/ffmpeg 3rdparty/pugixml asmjit 3rdparty/GSL 3rdparty/libpng Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers Utilities/yaml-cpp rsx_program_decompiler 3rdparty/cereal
|
||||
- 7z x wxWidgets.7z -aos -oC:\rpcs3\wxWidgets > null
|
||||
- 7z x zlib.7z -aos -oC:\rpcs3\ > null
|
||||
- 7z x vulkan.7z -aos -oC:\rpcs3\Vulkan > null
|
||||
- if %configuration%==Release (cmake -G "Visual Studio 14 Win64" -DZLIB_ROOT=C:/rpcs3/zlib/)
|
||||
- if %configuration%==Release (cmake -G "Visual Studio 14 Win64" -DZLIB_ROOT=C:/rpcs3/zlib/ -DVULKAN_PREBUILT=ON)
|
||||
else (7z x llvmlibs.7z -aos -oC:\rpcs3 > null && cmake -G "Visual Studio 14 Win64" -DLLVM_DIR=C:/rpcs3/llvm_build/share/llvm/cmake -DZLIB_ROOT=C:/rpcs3/zlib/ -DVULKAN_PREBUILT=ON)
|
||||
|
||||
build_script:
|
||||
@@ -31,4 +34,5 @@ install:
|
||||
|
||||
artifacts:
|
||||
- path: bin
|
||||
name: rpcs3-$(configuration)-$(COMMIT_SHA)
|
||||
name: 'rpcs3-v0.0.2-$(Date)-$(COMMIT_SHA)_win64'
|
||||
type: zip
|
||||
|
||||
@@ -2,5 +2,3 @@
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
||||
!cache
|
||||
!cache/README.txt
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
This directory is used for cache functionality, so don't store any data here.
|
||||
@@ -1 +0,0 @@
|
||||
This directory is used for temporary gamedata functionality, so don't store any data here.
|
||||
@@ -1,4 +1,4 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
||||
!.gitignore
|
||||
|
||||
+11
-3
@@ -1,6 +1,7 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake_modules")
|
||||
set(RES_FILES "")
|
||||
include(cotire)
|
||||
|
||||
project(rpcs3)
|
||||
@@ -15,9 +16,16 @@ add_custom_command(OUTPUT something_that_never_exists
|
||||
# Check for a sufficient compiler and set build options
|
||||
include(ConfigureCompiler)
|
||||
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-DUNICODE)
|
||||
add_definitions(-D_WIN32_WINNT=0x0601)
|
||||
set(RES_FILES "rpcs3_.rc")
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_RC_COMPILER_INIT windres)
|
||||
enable_language(RC)
|
||||
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> <FLAGS> -O coff <DEFINES> -i <SOURCE> -o <OBJECT>")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT MSVC)
|
||||
@@ -193,7 +201,7 @@ if(NOT WIN32)
|
||||
endforeach(TMP_PATH)
|
||||
endif()
|
||||
|
||||
add_executable(rpcs3 ${RPCS3_SRC})
|
||||
add_executable(rpcs3 ${RPCS3_SRC} ${RES_FILES})
|
||||
|
||||
|
||||
if(MSVC)
|
||||
@@ -201,7 +209,7 @@ if(MSVC)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /DYNAMICBASE:NO /BASE:0x10000 /FIXED")
|
||||
endif()
|
||||
if(WIN32)
|
||||
target_link_libraries(rpcs3 ws2_32.lib Winmm.lib VKstatic.1 glslang OSDependent OGLCompiler SPIRV HLSL)
|
||||
target_link_libraries(rpcs3 ws2_32.lib Winmm.lib Psapi.lib VKstatic.1 glslang OSDependent OGLCompiler SPIRV HLSL)
|
||||
if(NOT MSVC)
|
||||
target_link_libraries(rpcs3 ${OPENGL_LIBRARIES} ${GLEW_LIBRARY} opengl32.lib glu32.lib libpthread)
|
||||
else()
|
||||
@@ -210,7 +218,7 @@ if(WIN32)
|
||||
target_link_libraries(rpcs3 avformat.lib avcodec.lib avutil.lib swresample.lib swscale.lib png16_static ${wxWidgets_LIBRARIES} ${OPENAL_LIBRARY} ${ADDITIONAL_LIBS})
|
||||
else()
|
||||
target_link_libraries(rpcs3 ${wxWidgets_LIBRARIES} ${OPENAL_LIBRARY} ${GLEW_LIBRARY} ${OPENGL_LIBRARIES})
|
||||
target_link_libraries(rpcs3 ${ZLIB_LIBRARIES} ${ADDITIONAL_LIBS})
|
||||
target_link_libraries(rpcs3 -ldl ${ZLIB_LIBRARIES} ${ADDITIONAL_LIBS})
|
||||
if (USE_SYSTEM_FFMPEG)
|
||||
link_libraries(${FFMPEG_LIBRARY_DIR})
|
||||
target_link_libraries(rpcs3 libavformat.so libavcodec.so libavutil.so libswresample.so libswscale.so)
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define POLARSSL_AES_ROM_TABLES
|
||||
/**
|
||||
* \file aes.h
|
||||
*
|
||||
|
||||
@@ -140,6 +140,15 @@ static u8 VSH_PUB[0x28] = {
|
||||
0x6E, 0x73, 0x6A, 0xBF, 0x81, 0xF7, 0x0E, 0xE9, 0x16, 0x1B, 0x0D, 0xDE, 0xB0, 0x26, 0x76, 0x1A, 0xFF, 0x7B, 0xC8, 0x5B
|
||||
};
|
||||
|
||||
static u8 SCEPKG_RIV[0x10] = {
|
||||
0x4A, 0xCE, 0xF0, 0x12, 0x24, 0xFB, 0xEE, 0xDF, 0x82, 0x45, 0xF8, 0xFF, 0x10, 0x21, 0x1E, 0x6E
|
||||
};
|
||||
|
||||
static u8 SCEPKG_ERK[0x20] = {
|
||||
0xA9, 0x78, 0x18, 0xBD, 0x19, 0x3A, 0x67, 0xA1, 0x6F, 0xE8, 0x3A, 0x85, 0x5E, 0x1B, 0xE9, 0xFB, 0x56, 0x40, 0x93, 0x8D,
|
||||
0x4D, 0xBC, 0xB2, 0xCB, 0x52, 0xC5, 0xA2, 0xF8, 0xB0, 0x2B, 0x10, 0x31
|
||||
};
|
||||
|
||||
class KeyVault
|
||||
{
|
||||
std::vector<SELF_KEY> sk_LV0_arr;
|
||||
|
||||
+1
-2
@@ -140,8 +140,7 @@ int decompress(unsigned char *out, unsigned char *in, unsigned int size)
|
||||
unsigned int range = 0xFFFFFFFF;
|
||||
unsigned int code = (in[1] << 24) | (in[2] << 16) | (in[3] << 8) | in[4];
|
||||
|
||||
// TODO:: Syphurith: There was a check against the unsigned char head. if (head < 0) would always be false.. I don't know are you tried to if (head > 0x80)?
|
||||
if (head < 0) // Check if we have a valid starting byte.
|
||||
if (head > 0x80) // Check if we have a valid starting byte.
|
||||
{
|
||||
// The dictionary header is invalid, the data is not compressed.
|
||||
result = -1;
|
||||
|
||||
+454
-376
File diff suppressed because it is too large
Load Diff
+108
-25
@@ -2,35 +2,118 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <array>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#define SDAT_FLAG 0x01000000
|
||||
#define EDAT_COMPRESSED_FLAG 0x00000001
|
||||
#define EDAT_FLAG_0x02 0x00000002
|
||||
#define EDAT_ENCRYPTED_KEY_FLAG 0x00000008
|
||||
#define EDAT_FLAG_0x10 0x00000010
|
||||
#define EDAT_FLAG_0x20 0x00000020
|
||||
#define EDAT_DEBUG_DATA_FLAG 0x80000000
|
||||
constexpr u32 SDAT_FLAG = 0x01000000;
|
||||
constexpr u32 EDAT_COMPRESSED_FLAG = 0x00000001;
|
||||
constexpr u32 EDAT_FLAG_0x02 = 0x00000002;
|
||||
constexpr u32 EDAT_ENCRYPTED_KEY_FLAG = 0x00000008;
|
||||
constexpr u32 EDAT_FLAG_0x10 = 0x00000010;
|
||||
constexpr u32 EDAT_FLAG_0x20 = 0x00000020;
|
||||
constexpr u32 EDAT_DEBUG_DATA_FLAG = 0x80000000;
|
||||
|
||||
typedef struct
|
||||
struct EdatKeys_t
|
||||
{
|
||||
unsigned char magic[4];
|
||||
int version;
|
||||
int license;
|
||||
int type;
|
||||
unsigned char content_id[0x30];
|
||||
unsigned char digest[0x10];
|
||||
unsigned char title_hash[0x10];
|
||||
unsigned char dev_hash[0x10];
|
||||
unsigned long long unk1;
|
||||
unsigned long long unk2;
|
||||
} NPD_HEADER;
|
||||
std::array<u8, 0x10> devKlic{};
|
||||
std::array<u8, 0x10> rifKey{};
|
||||
};
|
||||
|
||||
typedef struct
|
||||
struct NPD_HEADER
|
||||
{
|
||||
int flags;
|
||||
int block_size;
|
||||
unsigned long long file_size;
|
||||
} EDAT_HEADER;
|
||||
u32 magic;
|
||||
s32 version;
|
||||
s32 license;
|
||||
s32 type;
|
||||
u8 content_id[0x30];
|
||||
u8 digest[0x10];
|
||||
u8 title_hash[0x10];
|
||||
u8 dev_hash[0x10];
|
||||
u64 unk1;
|
||||
u64 unk2;
|
||||
};
|
||||
|
||||
int DecryptEDAT(const std::string& input_file_name, const std::string& output_file_name, int mode, const std::string& rap_file_name, unsigned char *custom_klic, bool verbose);
|
||||
struct EDAT_HEADER
|
||||
{
|
||||
s32 flags;
|
||||
s32 block_size;
|
||||
u64 file_size;
|
||||
};
|
||||
|
||||
// Decrypts full file, or null/empty file
|
||||
extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name, int mode, const std::string& rap_file_name, u8 *custom_klic, bool verbose);
|
||||
|
||||
extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const std::array<u8,0x10>& custom_klic);
|
||||
|
||||
extern std::array<u8, 0x10> GetEdatRifKeyFromRapFile(const fs::file& rap_file);
|
||||
|
||||
struct EDATADecrypter final : fs::file_base
|
||||
{
|
||||
// file stream
|
||||
const fs::file edata_file;
|
||||
u64 file_size{0};
|
||||
u32 total_blocks{0};
|
||||
u64 pos{0};
|
||||
|
||||
NPD_HEADER npdHeader;
|
||||
EDAT_HEADER edatHeader;
|
||||
|
||||
// Internal data buffers.
|
||||
std::unique_ptr<u8[]> data_buf;
|
||||
u64 data_buf_size{0};
|
||||
|
||||
std::array<u8, 0x10> dec_key{};
|
||||
|
||||
// edat usage
|
||||
std::array<u8, 0x10> rif_key{};
|
||||
std::array<u8, 0x10> dev_key{};
|
||||
public:
|
||||
// SdataByFd usage
|
||||
EDATADecrypter(fs::file&& input)
|
||||
: edata_file(std::move(input)) {}
|
||||
// Edat usage
|
||||
EDATADecrypter(fs::file&& input, const std::array<u8, 0x10>& dev_key, const std::array<u8, 0x10>& rif_key)
|
||||
: edata_file(std::move(input)), rif_key(rif_key), dev_key(dev_key) {}
|
||||
|
||||
~EDATADecrypter() override {}
|
||||
// false if invalid
|
||||
bool ReadHeader();
|
||||
u64 ReadData(u64 pos, u8* data, u64 size);
|
||||
|
||||
fs::stat_t stat() override
|
||||
{
|
||||
fs::stat_t stats;
|
||||
stats.is_directory = false;
|
||||
stats.is_writable = false;
|
||||
stats.size = file_size;
|
||||
stats.atime = -1;
|
||||
stats.ctime = -1;
|
||||
stats.mtime = -1;
|
||||
return stats;
|
||||
}
|
||||
bool trunc(u64 length) override
|
||||
{
|
||||
return true;
|
||||
};
|
||||
u64 read(void* buffer, u64 size) override
|
||||
{
|
||||
u64 bytesRead = ReadData(pos, (u8*)buffer, size);
|
||||
pos += bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
u64 write(const void* buffer, u64 size) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 seek(s64 offset, fs::seek_mode whence) override
|
||||
{
|
||||
return
|
||||
whence == fs::seek_set ? pos = offset :
|
||||
whence == fs::seek_cur ? pos = offset + pos :
|
||||
whence == fs::seek_end ? pos = offset + size() :
|
||||
(fmt::raw_error("EDATADecrypter::seek(): invalid whence"), 0);
|
||||
}
|
||||
u64 size() override { return file_size; }
|
||||
};
|
||||
|
||||
+74
-11
@@ -49,12 +49,6 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
}
|
||||
}
|
||||
|
||||
if (header.header_size != PKG_HEADER_SIZE && header.header_size != PKG_HEADER_SIZE2)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Wrong PKG header size (0x%x)", header.header_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.pkg_size > pkg_f.size())
|
||||
{
|
||||
LOG_ERROR(LOADER, "PKG file size mismatch (pkg_size=0x%llx)", header.pkg_size);
|
||||
@@ -67,11 +61,54 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
return false;
|
||||
}
|
||||
|
||||
be_t<u32> drm_type{0};
|
||||
be_t<u32> content_type{0};
|
||||
|
||||
pkg_f.seek(header.pkg_info_off);
|
||||
|
||||
for (u32 i = 0; i < header.pkg_info_num; i++)
|
||||
{
|
||||
struct packet_T
|
||||
{
|
||||
be_t<u32> id;
|
||||
be_t<u32> size;
|
||||
} packet;
|
||||
|
||||
pkg_f.read(packet);
|
||||
|
||||
// TODO
|
||||
switch (+packet.id)
|
||||
{
|
||||
case 0x1:
|
||||
{
|
||||
if (packet.size == sizeof(drm_type))
|
||||
{
|
||||
pkg_f.read(drm_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 0x2:
|
||||
{
|
||||
if (packet.size == sizeof(content_type))
|
||||
{
|
||||
pkg_f.read(content_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pkg_f.seek(packet.size, fs::seek_cur);
|
||||
}
|
||||
|
||||
// Allocate buffer with BUF_SIZE size or more if required
|
||||
const std::unique_ptr<u128[]> buf(new u128[std::max<u64>(BUF_SIZE, sizeof(PKGEntry) * header.file_count) / sizeof(u128)]);
|
||||
|
||||
// Define decryption subfunction (`psp` arg selects the key for specific block)
|
||||
auto decrypt = [&](u64 offset, u64 size, bool psp) -> u64
|
||||
auto decrypt = [&](u64 offset, u64 size, const uchar* key) -> u64
|
||||
{
|
||||
pkg_f.seek(start_offset + header.data_offset + offset);
|
||||
|
||||
@@ -114,7 +151,7 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
aes_context ctx;
|
||||
|
||||
// Set encryption key for stream cipher
|
||||
aes_setkey_enc(&ctx, psp ? PKG_AES_KEY2 : PKG_AES_KEY, 128);
|
||||
aes_setkey_enc(&ctx, key, 128);
|
||||
|
||||
// Initialize stream cipher for start position
|
||||
be_t<u128> input = header.klicensee.value() + offset / 16;
|
||||
@@ -134,7 +171,24 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
return read;
|
||||
};
|
||||
|
||||
decrypt(0, header.file_count * sizeof(PKGEntry), header.pkg_platform == PKG_PLATFORM_TYPE_PSP);
|
||||
std::array<uchar, 16> dec_key;
|
||||
|
||||
if (header.pkg_platform == PKG_PLATFORM_TYPE_PSP && content_type >= 0x15 && content_type <= 0x17)
|
||||
{
|
||||
const uchar psp2t1[] = {0xE3, 0x1A, 0x70, 0xC9, 0xCE, 0x1D, 0xD7, 0x2B, 0xF3, 0xC0, 0x62, 0x29, 0x63, 0xF2, 0xEC, 0xCB};
|
||||
const uchar psp2t2[] = {0x42, 0x3A, 0xCA, 0x3A, 0x2B, 0xD5, 0x64, 0x9F, 0x96, 0x86, 0xAB, 0xAD, 0x6F, 0xD8, 0x80, 0x1F};
|
||||
const uchar psp2t3[] = {0xAF, 0x07, 0xFD, 0x59, 0x65, 0x25, 0x27, 0xBA, 0xF1, 0x33, 0x89, 0x66, 0x8B, 0x17, 0xD9, 0xEA};
|
||||
|
||||
aes_context ctx;
|
||||
aes_setkey_enc(&ctx, content_type == 0x15 ? psp2t1 : content_type == 0x16 ? psp2t2 : psp2t3, 128);
|
||||
aes_crypt_ecb(&ctx, AES_ENCRYPT, reinterpret_cast<const uchar*>(&header.klicensee), dec_key.data());
|
||||
decrypt(0, header.file_count * sizeof(PKGEntry), dec_key.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy(dec_key.data(), PKG_AES_KEY, dec_key.size());
|
||||
decrypt(0, header.file_count * sizeof(PKGEntry), header.pkg_platform == PKG_PLATFORM_TYPE_PSP ? PKG_AES_KEY2 : dec_key.data());
|
||||
}
|
||||
|
||||
std::vector<PKGEntry> entries(header.file_count);
|
||||
|
||||
@@ -150,10 +204,12 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
continue;
|
||||
}
|
||||
|
||||
decrypt(entry.name_offset, entry.name_size, is_psp);
|
||||
decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : dec_key.data());
|
||||
|
||||
const std::string name(reinterpret_cast<char*>(buf.get()), entry.name_size);
|
||||
|
||||
LOG_NOTICE(LOADER, "Entry 0x%08x: %s", entry.type, name);
|
||||
|
||||
switch (entry.type & 0xff)
|
||||
{
|
||||
case PKG_FILE_ENTRY_NPDRM:
|
||||
@@ -161,6 +217,12 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
case PKG_FILE_ENTRY_SDAT:
|
||||
case PKG_FILE_ENTRY_REGULAR:
|
||||
case PKG_FILE_ENTRY_UNK1:
|
||||
case 0xe:
|
||||
case 0x10:
|
||||
case 0x11:
|
||||
case 0x13:
|
||||
case 0x15:
|
||||
case 0x16:
|
||||
{
|
||||
const std::string path = dir + name;
|
||||
|
||||
@@ -172,7 +234,7 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
{
|
||||
const u64 block_size = std::min<u64>(BUF_SIZE, entry.file_size - pos);
|
||||
|
||||
if (decrypt(entry.file_offset + pos, block_size, is_psp) != block_size)
|
||||
if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : dec_key.data()) != block_size)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Failed to extract file %s", path);
|
||||
break;
|
||||
@@ -209,6 +271,7 @@ bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>
|
||||
}
|
||||
|
||||
case PKG_FILE_ENTRY_FOLDER:
|
||||
case 0x12:
|
||||
{
|
||||
const std::string path = dir + name;
|
||||
|
||||
|
||||
@@ -35,9 +35,9 @@ struct PKGHeader
|
||||
nse_t<u32> pkg_magic; // Magic (0x7f504b47)
|
||||
be_t<u16> pkg_type; // Release type (Retail:0x8000, Debug:0x0000)
|
||||
be_t<u16> pkg_platform; // Platform type (PS3:0x0001, PSP:0x0002)
|
||||
be_t<u32> header_size; // Header size (0xc0)
|
||||
be_t<u32> unk1; // Some PKG version maybe?
|
||||
be_t<u32> meta_size; // Size of metadata (block after header & hashes)
|
||||
be_t<u32> pkg_info_off;
|
||||
be_t<u32> pkg_info_num;
|
||||
be_t<u32> header_size; // Header size
|
||||
be_t<u32> file_count; // Number of files
|
||||
be_t<u64> pkg_size; // PKG size in bytes
|
||||
be_t<u64> data_offset; // Encrypted data offset
|
||||
|
||||
+287
-103
@@ -3,6 +3,7 @@
|
||||
#include "sha1.h"
|
||||
#include "utils.h"
|
||||
#include "unself.h"
|
||||
#include "Emu/VFS.h"
|
||||
|
||||
// TODO: Still reliant on wxWidgets for zlib functions. Alternative solutions?
|
||||
#include <zlib.h>
|
||||
@@ -647,6 +648,236 @@ void SelfHeader::Load(const fs::file& f)
|
||||
pad = Read64(f);
|
||||
}
|
||||
|
||||
SCEDecrypter::SCEDecrypter(const fs::file& s)
|
||||
: sce_f(s)
|
||||
, data_buf_length(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool SCEDecrypter::LoadHeaders()
|
||||
{
|
||||
// Read SCE header.
|
||||
sce_f.seek(0);
|
||||
sce_hdr.Load(sce_f);
|
||||
|
||||
// Check SCE magic.
|
||||
if (!sce_hdr.CheckMagic())
|
||||
{
|
||||
LOG_ERROR(LOADER, "SELF: Not a SELF file!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SCEDecrypter::LoadMetadata(const u8 erk[32], const u8 riv[16])
|
||||
{
|
||||
aes_context aes;
|
||||
u32 metadata_info_size = SIZE_32(meta_info);
|
||||
auto metadata_info = std::make_unique<u8[]>(metadata_info_size);
|
||||
u32 metadata_headers_size = sce_hdr.se_hsize - (SIZE_32(sce_hdr) + sce_hdr.se_meta + SIZE_32(meta_info));
|
||||
auto metadata_headers = std::make_unique<u8[]>(metadata_headers_size);
|
||||
|
||||
// Locate and read the encrypted metadata info.
|
||||
sce_f.seek(sce_hdr.se_meta + sizeof(sce_hdr));
|
||||
sce_f.read(metadata_info.get(), metadata_info_size);
|
||||
|
||||
// Locate and read the encrypted metadata header and section header.
|
||||
sce_f.seek(sce_hdr.se_meta + sizeof(sce_hdr) + metadata_info_size);
|
||||
sce_f.read(metadata_headers.get(), metadata_headers_size);
|
||||
|
||||
// Copy the necessary parameters.
|
||||
u8 metadata_key[0x20];
|
||||
u8 metadata_iv[0x10];
|
||||
memcpy(metadata_key, erk, 0x20);
|
||||
memcpy(metadata_iv, riv, 0x10);
|
||||
|
||||
// Check DEBUG flag.
|
||||
if ((sce_hdr.se_flags & 0x8000) != 0x8000)
|
||||
{
|
||||
// Decrypt the metadata info.
|
||||
aes_setkey_dec(&aes, metadata_key, 256); // AES-256
|
||||
aes_crypt_cbc(&aes, AES_DECRYPT, metadata_info_size, metadata_iv, metadata_info.get(), metadata_info.get());
|
||||
}
|
||||
|
||||
// Load the metadata info.
|
||||
meta_info.Load(metadata_info.get());
|
||||
|
||||
// If the padding is not NULL for the key or iv fields, the metadata info
|
||||
// is not properly decrypted.
|
||||
if ((meta_info.key_pad[0] != 0x00) ||
|
||||
(meta_info.iv_pad[0] != 0x00))
|
||||
{
|
||||
LOG_ERROR(LOADER, "SELF: Failed to decrypt metadata info!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform AES-CTR encryption on the metadata headers.
|
||||
size_t ctr_nc_off = 0;
|
||||
u8 ctr_stream_block[0x10];
|
||||
aes_setkey_enc(&aes, meta_info.key, 128);
|
||||
aes_crypt_ctr(&aes, metadata_headers_size, &ctr_nc_off, meta_info.iv, ctr_stream_block, metadata_headers.get(), metadata_headers.get());
|
||||
|
||||
// Load the metadata header.
|
||||
meta_hdr.Load(metadata_headers.get());
|
||||
|
||||
// Load the metadata section headers.
|
||||
meta_shdr.clear();
|
||||
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
|
||||
{
|
||||
meta_shdr.emplace_back();
|
||||
meta_shdr.back().Load(metadata_headers.get() + sizeof(meta_hdr) + sizeof(MetadataSectionHeader) * i);
|
||||
}
|
||||
|
||||
// Copy the decrypted data keys.
|
||||
data_keys_length = meta_hdr.key_count * 0x10;
|
||||
data_keys = std::make_unique<u8[]>(data_keys_length);
|
||||
memcpy(data_keys.get(), metadata_headers.get() + sizeof(meta_hdr) + meta_hdr.section_count * sizeof(MetadataSectionHeader), data_keys_length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SCEDecrypter::DecryptData()
|
||||
{
|
||||
aes_context aes;
|
||||
|
||||
// Calculate the total data size.
|
||||
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
|
||||
{
|
||||
data_buf_length += meta_shdr[i].data_size;
|
||||
}
|
||||
|
||||
// Allocate a buffer to store decrypted data.
|
||||
data_buf = std::make_unique<u8[]>(data_buf_length);
|
||||
|
||||
// Set initial offset.
|
||||
u32 data_buf_offset = 0;
|
||||
|
||||
// Parse the metadata section headers to find the offsets of encrypted data.
|
||||
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
|
||||
{
|
||||
size_t ctr_nc_off = 0;
|
||||
u8 ctr_stream_block[0x10];
|
||||
u8 data_key[0x10];
|
||||
u8 data_iv[0x10];
|
||||
|
||||
// Check if this is an encrypted section.
|
||||
if (meta_shdr[i].encrypted == 3)
|
||||
{
|
||||
// Make sure the key and iv are not out of boundaries.
|
||||
if ((meta_shdr[i].key_idx <= meta_hdr.key_count - 1) && (meta_shdr[i].iv_idx <= meta_hdr.key_count))
|
||||
{
|
||||
// Get the key and iv from the previously stored key buffer.
|
||||
memcpy(data_key, data_keys.get() + meta_shdr[i].key_idx * 0x10, 0x10);
|
||||
memcpy(data_iv, data_keys.get() + meta_shdr[i].iv_idx * 0x10, 0x10);
|
||||
|
||||
// Allocate a buffer to hold the data.
|
||||
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
|
||||
|
||||
// Seek to the section data offset and read the encrypted data.
|
||||
sce_f.seek(meta_shdr[i].data_offset);
|
||||
sce_f.read(buf.get(), meta_shdr[i].data_size);
|
||||
|
||||
// Zero out our ctr nonce.
|
||||
memset(ctr_stream_block, 0, sizeof(ctr_stream_block));
|
||||
|
||||
// Perform AES-CTR encryption on the data blocks.
|
||||
aes_setkey_enc(&aes, data_key, 128);
|
||||
aes_crypt_ctr(&aes, meta_shdr[i].data_size, &ctr_nc_off, data_iv, ctr_stream_block, buf.get(), buf.get());
|
||||
|
||||
// Copy the decrypted data.
|
||||
memcpy(data_buf.get() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
|
||||
sce_f.seek(meta_shdr[i].data_offset);
|
||||
sce_f.read(buf.get(), meta_shdr[i].data_size);
|
||||
memcpy(data_buf.get() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
|
||||
}
|
||||
|
||||
// Advance the buffer's offset.
|
||||
data_buf_offset += meta_shdr[i].data_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Each section gets put into its own file.
|
||||
std::vector<fs::file> SCEDecrypter::MakeFile()
|
||||
{
|
||||
std::vector<fs::file> vec;
|
||||
|
||||
// Set initial offset.
|
||||
u32 data_buf_offset = 0;
|
||||
|
||||
// Write data.
|
||||
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
|
||||
{
|
||||
fs::file out_f = fs::make_stream<std::vector<u8>>();
|
||||
|
||||
bool isValid = true;
|
||||
|
||||
// Decompress if necessary.
|
||||
if (meta_shdr[i].compressed == 2)
|
||||
{
|
||||
const size_t BUFSIZE = 32 * 1024;
|
||||
u8 tempbuf[BUFSIZE];
|
||||
z_stream strm;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = meta_shdr[i].data_size;
|
||||
strm.avail_out = BUFSIZE;
|
||||
strm.next_in = data_buf.get()+data_buf_offset;
|
||||
strm.next_out = tempbuf;
|
||||
int ret = inflateInit(&strm);
|
||||
|
||||
while (strm.avail_in)
|
||||
{
|
||||
ret = inflate(&strm, Z_NO_FLUSH);
|
||||
if (ret == Z_STREAM_END)
|
||||
break;
|
||||
if (ret != Z_OK)
|
||||
isValid = false;
|
||||
|
||||
if (!strm.avail_out) {
|
||||
out_f.write(tempbuf, BUFSIZE);
|
||||
strm.next_out = tempbuf;
|
||||
strm.avail_out = BUFSIZE;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
int inflate_res = Z_OK;
|
||||
inflate_res = inflate(&strm, Z_FINISH);
|
||||
|
||||
if (inflate_res != Z_STREAM_END)
|
||||
isValid = false;
|
||||
|
||||
out_f.write(tempbuf, BUFSIZE - strm.avail_out);
|
||||
inflateEnd(&strm);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write the data.
|
||||
out_f.write(data_buf.get()+data_buf_offset, meta_shdr[i].data_size);
|
||||
}
|
||||
|
||||
// Advance the data buffer offset by data size.
|
||||
data_buf_offset += meta_shdr[i].data_size;
|
||||
|
||||
if (out_f.pos() != out_f.size())
|
||||
fmt::throw_exception("MakeELF written bytes (%llu) does not equal buffer size (%llu).", out_f.pos(), out_f.size());
|
||||
|
||||
if (isValid) vec.push_back(std::move(out_f));
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
SELFDecrypter::SELFDecrypter(const fs::file& s)
|
||||
: self_f(s)
|
||||
, key_v()
|
||||
@@ -904,17 +1135,17 @@ bool SELFDecrypter::LoadMetadata()
|
||||
{
|
||||
aes_context aes;
|
||||
u32 metadata_info_size = SIZE_32(meta_info);
|
||||
u8 *metadata_info = (u8 *)malloc(metadata_info_size);
|
||||
auto metadata_info = std::make_unique<u8[]>(metadata_info_size);
|
||||
u32 metadata_headers_size = sce_hdr.se_hsize - (SIZE_32(sce_hdr) + sce_hdr.se_meta + SIZE_32(meta_info));
|
||||
u8 *metadata_headers = (u8 *)malloc(metadata_headers_size);
|
||||
auto metadata_headers = std::make_unique<u8[]>(metadata_headers_size);
|
||||
|
||||
// Locate and read the encrypted metadata info.
|
||||
self_f.seek(sce_hdr.se_meta + sizeof(sce_hdr));
|
||||
self_f.read(metadata_info, metadata_info_size);
|
||||
self_f.read(metadata_info.get(), metadata_info_size);
|
||||
|
||||
// Locate and read the encrypted metadata header and section header.
|
||||
self_f.seek(sce_hdr.se_meta + sizeof(sce_hdr) + metadata_info_size);
|
||||
self_f.read(metadata_headers, metadata_headers_size);
|
||||
self_f.read(metadata_headers.get(), metadata_headers_size);
|
||||
|
||||
// Find the right keyset from the key vault.
|
||||
SELF_KEY keyset = key_v.FindSelfKey(app_info.self_type, sce_hdr.se_flags, app_info.version);
|
||||
@@ -929,16 +1160,16 @@ bool SELFDecrypter::LoadMetadata()
|
||||
if ((sce_hdr.se_flags & 0x8000) != 0x8000)
|
||||
{
|
||||
// Decrypt the NPDRM layer.
|
||||
if (!DecryptNPDRM(metadata_info, metadata_info_size))
|
||||
if (!DecryptNPDRM(metadata_info.get(), metadata_info_size))
|
||||
return false;
|
||||
|
||||
// Decrypt the metadata info.
|
||||
aes_setkey_dec(&aes, metadata_key, 256); // AES-256
|
||||
aes_crypt_cbc(&aes, AES_DECRYPT, metadata_info_size, metadata_iv, metadata_info, metadata_info);
|
||||
aes_crypt_cbc(&aes, AES_DECRYPT, metadata_info_size, metadata_iv, metadata_info.get(), metadata_info.get());
|
||||
}
|
||||
|
||||
// Load the metadata info.
|
||||
meta_info.Load(metadata_info);
|
||||
meta_info.Load(metadata_info.get());
|
||||
|
||||
// If the padding is not NULL for the key or iv fields, the metadata info
|
||||
// is not properly decrypted.
|
||||
@@ -953,23 +1184,23 @@ bool SELFDecrypter::LoadMetadata()
|
||||
size_t ctr_nc_off = 0;
|
||||
u8 ctr_stream_block[0x10];
|
||||
aes_setkey_enc(&aes, meta_info.key, 128);
|
||||
aes_crypt_ctr(&aes, metadata_headers_size, &ctr_nc_off, meta_info.iv, ctr_stream_block, metadata_headers, metadata_headers);
|
||||
aes_crypt_ctr(&aes, metadata_headers_size, &ctr_nc_off, meta_info.iv, ctr_stream_block, metadata_headers.get(), metadata_headers.get());
|
||||
|
||||
// Load the metadata header.
|
||||
meta_hdr.Load(metadata_headers);
|
||||
meta_hdr.Load(metadata_headers.get());
|
||||
|
||||
// Load the metadata section headers.
|
||||
meta_shdr.clear();
|
||||
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
|
||||
{
|
||||
meta_shdr.emplace_back();
|
||||
meta_shdr.back().Load(metadata_headers + sizeof(meta_hdr) + sizeof(MetadataSectionHeader) * i);
|
||||
meta_shdr.back().Load(metadata_headers.get() + sizeof(meta_hdr) + sizeof(MetadataSectionHeader) * i);
|
||||
}
|
||||
|
||||
// Copy the decrypted data keys.
|
||||
data_keys_length = meta_hdr.key_count * 0x10;
|
||||
data_keys = (u8 *) malloc (data_keys_length);
|
||||
memcpy(data_keys, metadata_headers + sizeof(meta_hdr) + meta_hdr.section_count * sizeof(MetadataSectionHeader), data_keys_length);
|
||||
data_keys = std::make_unique<u8[]>(data_keys_length);
|
||||
memcpy(data_keys.get(), metadata_headers.get() + sizeof(meta_hdr) + meta_hdr.section_count * sizeof(MetadataSectionHeader), data_keys_length);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -989,7 +1220,7 @@ bool SELFDecrypter::DecryptData()
|
||||
}
|
||||
|
||||
// Allocate a buffer to store decrypted data.
|
||||
data_buf = (u8*)malloc(data_buf_length);
|
||||
data_buf = std::make_unique<u8[]>(data_buf_length);
|
||||
|
||||
// Set initial offset.
|
||||
u32 data_buf_offset = 0;
|
||||
@@ -1009,31 +1240,28 @@ bool SELFDecrypter::DecryptData()
|
||||
if((meta_shdr[i].key_idx <= meta_hdr.key_count - 1) && (meta_shdr[i].iv_idx <= meta_hdr.key_count))
|
||||
{
|
||||
// Get the key and iv from the previously stored key buffer.
|
||||
memcpy(data_key, data_keys + meta_shdr[i].key_idx * 0x10, 0x10);
|
||||
memcpy(data_iv, data_keys + meta_shdr[i].iv_idx * 0x10, 0x10);
|
||||
memcpy(data_key, data_keys.get() + meta_shdr[i].key_idx * 0x10, 0x10);
|
||||
memcpy(data_iv, data_keys.get() + meta_shdr[i].iv_idx * 0x10, 0x10);
|
||||
|
||||
// Allocate a buffer to hold the data.
|
||||
u8 *buf = (u8 *)malloc(meta_shdr[i].data_size);
|
||||
auto buf = std::make_unique<u8[]>(meta_shdr[i].data_size);
|
||||
|
||||
// Seek to the section data offset and read the encrypted data.
|
||||
self_f.seek(meta_shdr[i].data_offset);
|
||||
self_f.read(buf, meta_shdr[i].data_size);
|
||||
self_f.read(buf.get(), meta_shdr[i].data_size);
|
||||
|
||||
// Zero out our ctr nonce.
|
||||
memset(ctr_stream_block, 0, sizeof(ctr_stream_block));
|
||||
|
||||
// Perform AES-CTR encryption on the data blocks.
|
||||
aes_setkey_enc(&aes, data_key, 128);
|
||||
aes_crypt_ctr(&aes, meta_shdr[i].data_size, &ctr_nc_off, data_iv, ctr_stream_block, buf, buf);
|
||||
aes_crypt_ctr(&aes, meta_shdr[i].data_size, &ctr_nc_off, data_iv, ctr_stream_block, buf.get(), buf.get());
|
||||
|
||||
// Copy the decrypted data.
|
||||
memcpy(data_buf + data_buf_offset, buf, meta_shdr[i].data_size);
|
||||
memcpy(data_buf.get() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
|
||||
|
||||
// Advance the buffer's offset.
|
||||
data_buf_offset += meta_shdr[i].data_size;
|
||||
|
||||
// Release the temporary buffer.
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1041,15 +1269,10 @@ bool SELFDecrypter::DecryptData()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SELFDecrypter::MakeElf(const std::string& elf, bool isElf32)
|
||||
fs::file SELFDecrypter::MakeElf(bool isElf32)
|
||||
{
|
||||
// Create a new ELF file.
|
||||
fs::file e(elf, fs::rewrite);
|
||||
if(!e)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Could not create ELF file! (%s)", elf.c_str());
|
||||
return false;
|
||||
}
|
||||
fs::file e = fs::make_stream<std::vector<u8>>();
|
||||
|
||||
// Set initial offset.
|
||||
u32 data_buf_offset = 0;
|
||||
@@ -1072,7 +1295,7 @@ bool SELFDecrypter::MakeElf(const std::string& elf, bool isElf32)
|
||||
{
|
||||
// Seek to the program header data offset and write the data.
|
||||
e.seek(phdr32_arr[meta_shdr[i].program_idx].p_offset);
|
||||
e.write(data_buf + data_buf_offset, meta_shdr[i].data_size);
|
||||
e.write(data_buf.get() + data_buf_offset, meta_shdr[i].data_size);
|
||||
|
||||
// Advance the data buffer offset by data size.
|
||||
data_buf_offset += meta_shdr[i].data_size;
|
||||
@@ -1122,7 +1345,7 @@ bool SELFDecrypter::MakeElf(const std::string& elf, bool isElf32)
|
||||
|
||||
// Create a buffer separate from data_buf to uncompress.
|
||||
std::unique_ptr<u8[]> zlib_buf(new u8[data_buf_length]);
|
||||
memcpy(zlib_buf.get(), data_buf, data_buf_length);
|
||||
memcpy(zlib_buf.get(), data_buf.get(), data_buf_length);
|
||||
|
||||
// Use zlib uncompress on the new buffer.
|
||||
// decomp_buf_length changes inside the call to uncompress, so it must be a pointer to correct type (in writeable mem space).
|
||||
@@ -1145,7 +1368,7 @@ bool SELFDecrypter::MakeElf(const std::string& elf, bool isElf32)
|
||||
{
|
||||
// Seek to the program header data offset and write the data.
|
||||
e.seek(phdr64_arr[meta_shdr[i].program_idx].p_offset);
|
||||
e.write(data_buf + data_buf_offset, meta_shdr[i].data_size);
|
||||
e.write(data_buf.get() + data_buf_offset, meta_shdr[i].data_size);
|
||||
}
|
||||
|
||||
// Advance the data buffer offset by data size.
|
||||
@@ -1165,33 +1388,25 @@ bool SELFDecrypter::MakeElf(const std::string& elf, bool isElf32)
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return e;
|
||||
}
|
||||
|
||||
bool SELFDecrypter::GetKeyFromRap(u8 *content_id, u8 *npdrm_key)
|
||||
bool SELFDecrypter::GetKeyFromRap(u8* content_id, u8* npdrm_key)
|
||||
{
|
||||
// Set empty RAP key.
|
||||
u8 rap_key[0x10];
|
||||
memset(rap_key, 0, 0x10);
|
||||
|
||||
// Try to find a matching RAP file under exdata folder.
|
||||
std::string ci_str((const char *)content_id);
|
||||
std::string pf_str("00000001"); // TODO: Allow multiple profiles. Use default for now.
|
||||
std::string rap_path("dev_hdd0/home/" + pf_str + "/exdata/" + ci_str + ".rap");
|
||||
|
||||
// Check if we have a valid RAP file.
|
||||
if (!fs::is_file(rap_path))
|
||||
{
|
||||
LOG_ERROR(LOADER, "This application requires a valid RAP file for decryption!");
|
||||
return false;
|
||||
}
|
||||
const std::string ci_str = reinterpret_cast<const char*>(content_id);
|
||||
const std::string rap_path = "/dev_hdd0/home/00000001/exdata/" + ci_str + ".rap";
|
||||
|
||||
// Open the RAP file and read the key.
|
||||
fs::file rap_file(rap_path);
|
||||
const fs::file rap_file(vfs::get(rap_path));
|
||||
|
||||
if (!rap_file)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Failed to load RAP file!");
|
||||
LOG_FATAL(LOADER, "Failed to load RAP file: %s", rap_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1204,23 +1419,11 @@ bool SELFDecrypter::GetKeyFromRap(u8 *content_id, u8 *npdrm_key)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsSelf(const std::string& path)
|
||||
static bool IsSelfElf32(const fs::file& f)
|
||||
{
|
||||
fs::file f(path);
|
||||
|
||||
if (!f) return false;
|
||||
|
||||
SceHeader hdr;
|
||||
hdr.Load(f);
|
||||
|
||||
return hdr.CheckMagic();
|
||||
}
|
||||
|
||||
bool IsSelfElf32(const std::string& path)
|
||||
{
|
||||
fs::file f(path);
|
||||
|
||||
if (!f) return false;
|
||||
f.seek(0);
|
||||
|
||||
SceHeader hdr;
|
||||
SelfHeader sh;
|
||||
@@ -1236,46 +1439,31 @@ bool IsSelfElf32(const std::string& path)
|
||||
return (elf_class[4] == 1);
|
||||
}
|
||||
|
||||
bool CheckDebugSelf(const std::string& self, const std::string& elf)
|
||||
static bool CheckDebugSelf(fs::file& s)
|
||||
{
|
||||
// Open the SELF file.
|
||||
fs::file s(self);
|
||||
|
||||
if (!s)
|
||||
if (s.size() < 0x18)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Could not open SELF file! (%s)", self.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the key version.
|
||||
s.seek(0x08);
|
||||
|
||||
u16 key_version;
|
||||
s.read(&key_version, sizeof(key_version));
|
||||
const u16 key_version = s.read<le_t<u16>>();
|
||||
|
||||
// Check for DEBUG version.
|
||||
if (swap16(key_version) == 0x8000)
|
||||
if (key_version == 0x80 || key_version == 0xc0)
|
||||
{
|
||||
LOG_WARNING(LOADER, "Debug SELF detected! Removing fake header...");
|
||||
|
||||
// Get the real elf offset.
|
||||
s.seek(0x10);
|
||||
|
||||
u64 elf_offset;
|
||||
s.read(&elf_offset, sizeof(elf_offset));
|
||||
|
||||
// Start at the real elf offset.
|
||||
elf_offset = swap64(elf_offset);
|
||||
|
||||
s.seek(elf_offset);
|
||||
s.seek(key_version == 0x80 ? +s.read<be_t<u64>>() : +s.read<le_t<u64>>());
|
||||
|
||||
// Write the real ELF file back.
|
||||
fs::file e(elf, fs::rewrite);
|
||||
if (!e)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Could not create ELF file! (%s)", elf.c_str());
|
||||
return false;
|
||||
}
|
||||
fs::file e = fs::make_stream<std::vector<u8>>();
|
||||
|
||||
// Copy the data.
|
||||
char buf[2048];
|
||||
@@ -1284,6 +1472,7 @@ bool CheckDebugSelf(const std::string& self, const std::string& elf)
|
||||
e.write(buf, size);
|
||||
}
|
||||
|
||||
s = std::move(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1291,53 +1480,48 @@ bool CheckDebugSelf(const std::string& self, const std::string& elf)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DecryptSelf(const std::string& elf, const std::string& self)
|
||||
{
|
||||
LOG_NOTICE(LOADER, "Decrypting %s", self);
|
||||
|
||||
// Check for a debug SELF first.
|
||||
if (!CheckDebugSelf(self, elf))
|
||||
extern fs::file decrypt_self(fs::file elf_or_self)
|
||||
{
|
||||
if (!elf_or_self)
|
||||
{
|
||||
// Set a virtual pointer to the SELF file.
|
||||
fs::file self_vf(self);
|
||||
|
||||
if (!self_vf)
|
||||
return false;
|
||||
return fs::file{};
|
||||
}
|
||||
|
||||
elf_or_self.seek(0);
|
||||
|
||||
// Check SELF header first. Check for a debug SELF.
|
||||
if (elf_or_self.size() >= 4 && elf_or_self.read<u32>() == "SCE\0"_u32 && !CheckDebugSelf(elf_or_self))
|
||||
{
|
||||
// Check the ELF file class (32 or 64 bit).
|
||||
bool isElf32 = IsSelfElf32(self);
|
||||
bool isElf32 = IsSelfElf32(elf_or_self);
|
||||
|
||||
// Start the decrypter on this SELF file.
|
||||
SELFDecrypter self_dec(self_vf);
|
||||
SELFDecrypter self_dec(elf_or_self);
|
||||
|
||||
// Load the SELF file headers.
|
||||
if (!self_dec.LoadHeaders(isElf32))
|
||||
{
|
||||
LOG_ERROR(LOADER, "SELF: Failed to load SELF file headers!");
|
||||
return false;
|
||||
return fs::file{};
|
||||
}
|
||||
|
||||
// Load and decrypt the SELF file metadata.
|
||||
if (!self_dec.LoadMetadata())
|
||||
{
|
||||
LOG_ERROR(LOADER, "SELF: Failed to load SELF file metadata!");
|
||||
return false;
|
||||
return fs::file{};
|
||||
}
|
||||
|
||||
// Decrypt the SELF file data.
|
||||
if (!self_dec.DecryptData())
|
||||
{
|
||||
LOG_ERROR(LOADER, "SELF: Failed to decrypt SELF file data!");
|
||||
return false;
|
||||
return fs::file{};
|
||||
}
|
||||
|
||||
// Make a new ELF file from this SELF.
|
||||
if (!self_dec.MakeElf(elf, isElf32))
|
||||
{
|
||||
LOG_ERROR(LOADER, "SELF: Failed to make ELF file from SELF!");
|
||||
return false;
|
||||
}
|
||||
return self_dec.MakeElf(isElf32);
|
||||
}
|
||||
|
||||
return true;
|
||||
return elf_or_self;
|
||||
}
|
||||
|
||||
+32
-7
@@ -338,6 +338,34 @@ struct SelfHeader
|
||||
void Show(){}
|
||||
};
|
||||
|
||||
class SCEDecrypter
|
||||
{
|
||||
protected:
|
||||
// Main SELF file stream.
|
||||
const fs::file& sce_f;
|
||||
|
||||
// SCE headers.
|
||||
SceHeader sce_hdr;
|
||||
|
||||
// Metadata structs.
|
||||
MetadataInfo meta_info;
|
||||
MetadataHeader meta_hdr;
|
||||
std::vector<MetadataSectionHeader> meta_shdr;
|
||||
|
||||
// Internal data buffers.
|
||||
std::unique_ptr<u8[]> data_keys;
|
||||
u32 data_keys_length;
|
||||
std::unique_ptr<u8[]> data_buf;
|
||||
u32 data_buf_length;
|
||||
|
||||
public:
|
||||
SCEDecrypter(const fs::file& s);
|
||||
std::vector<fs::file> MakeFile();
|
||||
bool LoadHeaders();
|
||||
bool LoadMetadata(const u8 erk[32], const u8 riv[16]);
|
||||
bool DecryptData();
|
||||
};
|
||||
|
||||
class SELFDecrypter
|
||||
{
|
||||
// Main SELF file stream.
|
||||
@@ -369,9 +397,9 @@ class SELFDecrypter
|
||||
std::vector<MetadataSectionHeader> meta_shdr;
|
||||
|
||||
// Internal data buffers.
|
||||
u8 *data_keys;
|
||||
std::unique_ptr<u8[]> data_keys;
|
||||
u32 data_keys_length;
|
||||
u8 *data_buf;
|
||||
std::unique_ptr<u8[]> data_buf;
|
||||
u32 data_buf_length;
|
||||
|
||||
// Main key vault instance.
|
||||
@@ -379,7 +407,7 @@ class SELFDecrypter
|
||||
|
||||
public:
|
||||
SELFDecrypter(const fs::file& s);
|
||||
bool MakeElf(const std::string& elf, bool isElf32);
|
||||
fs::file MakeElf(bool isElf32);
|
||||
bool LoadHeaders(bool isElf32);
|
||||
void ShowHeaders(bool isElf32);
|
||||
bool LoadMetadata();
|
||||
@@ -388,7 +416,4 @@ public:
|
||||
bool GetKeyFromRap(u8 *content_id, u8 *npdrm_key);
|
||||
};
|
||||
|
||||
extern bool IsSelf(const std::string& path);
|
||||
extern bool IsSelfElf32(const std::string& path);
|
||||
extern bool CheckDebugSelf(const std::string& self, const std::string& elf);
|
||||
extern bool DecryptSelf(const std::string& elf, const std::string& self);
|
||||
extern fs::file decrypt_self(fs::file elf_or_self);
|
||||
|
||||
+21
-66
@@ -6,29 +6,13 @@
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
// Auxiliary functions (endian swap, xor and prng).
|
||||
u16 swap16(u16 i)
|
||||
{
|
||||
return ((i & 0xFF00) >> 8) | ((i & 0xFF) << 8);
|
||||
}
|
||||
|
||||
u32 swap32(u32 i)
|
||||
void xor_key(unsigned char *dest, const u8* src1, const u8* src2)
|
||||
{
|
||||
return ((i & 0xFF000000) >> 24) | ((i & 0xFF0000) >> 8) | ((i & 0xFF00) << 8) | ((i & 0xFF) << 24);
|
||||
}
|
||||
|
||||
u64 swap64(u64 i)
|
||||
{
|
||||
return ((i & 0x00000000000000ff) << 56) | ((i & 0x000000000000ff00) << 40) |
|
||||
((i & 0x0000000000ff0000) << 24) | ((i & 0x00000000ff000000) << 8) |
|
||||
((i & 0x000000ff00000000) >> 8) | ((i & 0x0000ff0000000000) >> 24) |
|
||||
((i & 0x00ff000000000000) >> 40) | ((i & 0xff00000000000000) >> 56);
|
||||
}
|
||||
|
||||
void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2, int size)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < size; i++)
|
||||
for(int i = 0; i < 0x10; i++)
|
||||
{
|
||||
dest[i] = src1[i] ^ src2[i];
|
||||
}
|
||||
@@ -36,16 +20,10 @@ void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2, int
|
||||
|
||||
void prng(unsigned char *dest, int size)
|
||||
{
|
||||
unsigned char *buffer = new unsigned char[size];
|
||||
srand((u32)time(0));
|
||||
|
||||
int i;
|
||||
for(i = 0; i < size; i++)
|
||||
buffer[i] = (unsigned char)(rand() & 0xFF);
|
||||
|
||||
memcpy(dest, buffer, size);
|
||||
|
||||
delete[] buffer;
|
||||
for(int i = 0; i < size; i++)
|
||||
dest[i] = (unsigned char)(rand() & 0xFF);
|
||||
}
|
||||
|
||||
// Hex string conversion auxiliary functions.
|
||||
@@ -82,8 +60,8 @@ void hex_to_bytes(unsigned char *data, const char *hex_str, unsigned int str_len
|
||||
// Don't convert if the string length is odd.
|
||||
if (!(strn_length % 2))
|
||||
{
|
||||
u8 *out = (u8 *)malloc(strn_length * sizeof(u8));
|
||||
u8 *pos = out;
|
||||
auto out = std::make_unique<u8[]>(strn_length * sizeof(u8));
|
||||
u8 *pos = out.get();
|
||||
|
||||
while (strn_length--)
|
||||
{
|
||||
@@ -94,26 +72,25 @@ void hex_to_bytes(unsigned char *data, const char *hex_str, unsigned int str_len
|
||||
}
|
||||
|
||||
// Copy back to our array.
|
||||
memcpy(data, out, data_length);
|
||||
free(out);
|
||||
memcpy(data, out.get(), data_length);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_hex(const char* hex_str, unsigned int str_length)
|
||||
{
|
||||
static const char hex_chars[] = "0123456789abcdefABCDEF";
|
||||
static const char hex_chars[] = "0123456789abcdefABCDEF";
|
||||
|
||||
if (hex_str == NULL)
|
||||
return false;
|
||||
if (hex_str == NULL)
|
||||
return false;
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < str_length; i++)
|
||||
unsigned int i;
|
||||
for (i = 0; i < str_length; i++)
|
||||
{
|
||||
if (strchr(hex_chars, hex_str[i]) == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Crypto functions (AES128-CBC, AES128-ECB, SHA1-HMAC and AES-CMAC).
|
||||
@@ -146,22 +123,11 @@ void aesecb128_encrypt(unsigned char *key, unsigned char *in, unsigned char *out
|
||||
|
||||
bool hmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len)
|
||||
{
|
||||
unsigned char *out = new unsigned char[key_len];
|
||||
std::unique_ptr<u8> out(new u8[key_len]);
|
||||
|
||||
sha1_hmac(key, key_len, in, in_len, out);
|
||||
sha1_hmac(key, key_len, in, in_len, out.get());
|
||||
|
||||
for (int i = 0; i < hash_len; i++)
|
||||
{
|
||||
if (out[i] != hash[i])
|
||||
{
|
||||
delete[] out;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] out;
|
||||
|
||||
return true;
|
||||
return std::memcmp(out.get(), hash, hash_len) == 0;
|
||||
}
|
||||
|
||||
void hmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash)
|
||||
@@ -171,24 +137,13 @@ void hmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_
|
||||
|
||||
bool cmac_hash_compare(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash, int hash_len)
|
||||
{
|
||||
unsigned char *out = new unsigned char[key_len];
|
||||
std::unique_ptr<u8> out(new u8[key_len]);
|
||||
|
||||
aes_context ctx;
|
||||
aes_setkey_enc(&ctx, key, 128);
|
||||
aes_cmac(&ctx, in_len, in, out);
|
||||
aes_cmac(&ctx, in_len, in, out.get());
|
||||
|
||||
for (int i = 0; i < hash_len; i++)
|
||||
{
|
||||
if (out[i] != hash[i])
|
||||
{
|
||||
delete[] out;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] out;
|
||||
|
||||
return true;
|
||||
return std::memcmp(out.get(), hash, hash_len) == 0;
|
||||
}
|
||||
|
||||
void cmac_hash_forge(unsigned char *key, int key_len, unsigned char *in, int in_len, unsigned char *hash)
|
||||
|
||||
+34
-4
@@ -15,10 +15,40 @@
|
||||
#include "ec.h"
|
||||
|
||||
// Auxiliary functions (endian swap, xor, prng and file name).
|
||||
u16 swap16(u16 i);
|
||||
u32 swap32(u32 i);
|
||||
u64 swap64(u64 i);
|
||||
void xor_key(unsigned char *dest, unsigned char *src1, unsigned char *src2, int size);
|
||||
inline u16 swap16(u16 i)
|
||||
{
|
||||
#if defined(__GNUG__)
|
||||
return __builtin_bswap16(i);
|
||||
#else
|
||||
return _byteswap_ushort(i);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u32 swap32(u32 i)
|
||||
{
|
||||
#if defined(__GNUG__)
|
||||
return __builtin_bswap32(i);
|
||||
#else
|
||||
return _byteswap_ulong(i);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 swap64(u64 i)
|
||||
{
|
||||
#if defined(__GNUG__)
|
||||
return __builtin_bswap64(i);
|
||||
#else
|
||||
return _byteswap_uint64(i);
|
||||
#endif
|
||||
}
|
||||
|
||||
void xor_key(unsigned char *dest, const u8* src1, const u8* src2);
|
||||
inline void xor_key_sse(u8* dest, const u8* src1, const u8* src2)
|
||||
{
|
||||
_mm_storeu_si128(&(((__m128i*)dest)[0]),
|
||||
_mm_xor_si128(_mm_loadu_si128((__m128i*)src1), _mm_loadu_si128((__m128i*)src2)));
|
||||
}
|
||||
|
||||
void prng(unsigned char *dest, int size);
|
||||
char* extract_file_name(const char* file_path, char real_file_name[MAX_PATH]);
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
#include "stdafx.h"
|
||||
#include "AudioDumper.h"
|
||||
#include "AudioThread.h"
|
||||
|
||||
AudioThread::~AudioThread()
|
||||
{
|
||||
}
|
||||
|
||||
AudioDumper::AudioDumper(u16 ch)
|
||||
: m_header(ch)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
class AudioThread
|
||||
{
|
||||
public:
|
||||
virtual ~AudioThread() = default;
|
||||
virtual ~AudioThread();
|
||||
|
||||
virtual void Play() = 0;
|
||||
virtual void Open(const void* src, int size) = 0;
|
||||
|
||||
+57
-43
@@ -1,25 +1,28 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "CPUThread.h"
|
||||
|
||||
#include <mutex>
|
||||
DECLARE(cpu_thread::g_threads_created){0};
|
||||
DECLARE(cpu_thread::g_threads_deleted){0};
|
||||
|
||||
template<>
|
||||
template <>
|
||||
void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](cpu_flag f)
|
||||
{
|
||||
switch (f)
|
||||
{
|
||||
STR_CASE(cpu_flag::stop);
|
||||
STR_CASE(cpu_flag::exit);
|
||||
STR_CASE(cpu_flag::suspend);
|
||||
STR_CASE(cpu_flag::ret);
|
||||
STR_CASE(cpu_flag::signal);
|
||||
STR_CASE(cpu_flag::dbg_global_pause);
|
||||
STR_CASE(cpu_flag::dbg_global_stop);
|
||||
STR_CASE(cpu_flag::dbg_pause);
|
||||
STR_CASE(cpu_flag::dbg_step);
|
||||
case cpu_flag::stop: return "STOP";
|
||||
case cpu_flag::exit: return "EXIT";
|
||||
case cpu_flag::suspend: return "s";
|
||||
case cpu_flag::ret: return "ret";
|
||||
case cpu_flag::signal: return "sig";
|
||||
case cpu_flag::memory: return "mem";
|
||||
case cpu_flag::dbg_global_pause: return "G-PAUSE";
|
||||
case cpu_flag::dbg_global_stop: return "G-EXIT";
|
||||
case cpu_flag::dbg_pause: return "PAUSE";
|
||||
case cpu_flag::dbg_step: return "STEP";
|
||||
case cpu_flag::__bitset_enum_max: break;
|
||||
}
|
||||
|
||||
@@ -41,20 +44,12 @@ void cpu_thread::on_task()
|
||||
|
||||
g_tls_current_cpu_thread = this;
|
||||
|
||||
Emu.SendDbgCommand(DID_CREATE_THREAD, this);
|
||||
|
||||
std::unique_lock<named_thread> lock(*this);
|
||||
|
||||
// Check thread status
|
||||
while (!test(state & cpu_flag::exit))
|
||||
while (!test(state, cpu_flag::exit + cpu_flag::dbg_global_stop))
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
|
||||
// check stop status
|
||||
// Check stop status
|
||||
if (!test(state & cpu_flag::stop))
|
||||
{
|
||||
if (lock) lock.unlock();
|
||||
|
||||
try
|
||||
{
|
||||
cpu_task();
|
||||
@@ -73,12 +68,6 @@ void cpu_thread::on_task()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!lock)
|
||||
{
|
||||
lock.lock();
|
||||
continue;
|
||||
}
|
||||
|
||||
thread_ctrl::wait();
|
||||
}
|
||||
}
|
||||
@@ -86,38 +75,58 @@ void cpu_thread::on_task()
|
||||
void cpu_thread::on_stop()
|
||||
{
|
||||
state += cpu_flag::exit;
|
||||
lock_notify();
|
||||
notify();
|
||||
}
|
||||
|
||||
cpu_thread::~cpu_thread()
|
||||
{
|
||||
vm::cleanup_unlock(*this);
|
||||
g_threads_deleted++;
|
||||
}
|
||||
|
||||
cpu_thread::cpu_thread()
|
||||
cpu_thread::cpu_thread(u32 id)
|
||||
: id(id)
|
||||
{
|
||||
g_threads_created++;
|
||||
}
|
||||
|
||||
bool cpu_thread::check_state()
|
||||
{
|
||||
std::unique_lock<named_thread> lock(*this, std::defer_lock);
|
||||
bool cpu_sleep_called = false;
|
||||
bool cpu_flag_memory = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
CHECK_EMU_STATUS; // check at least once
|
||||
if (test(state, cpu_flag::memory) && state.test_and_reset(cpu_flag::memory))
|
||||
{
|
||||
cpu_flag_memory = true;
|
||||
|
||||
if (test(state & cpu_flag::exit))
|
||||
if (auto& ptr = vm::g_tls_locked)
|
||||
{
|
||||
ptr->compare_and_swap(this, nullptr);
|
||||
ptr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (test(state, cpu_flag::exit + cpu_flag::dbg_global_stop))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!test(state & cpu_state_pause))
|
||||
if (test(state & cpu_flag::signal) && state.test_and_reset(cpu_flag::signal))
|
||||
{
|
||||
break;
|
||||
cpu_sleep_called = false;
|
||||
}
|
||||
|
||||
if (!lock)
|
||||
if (!test(state, cpu_state_pause))
|
||||
{
|
||||
lock.lock();
|
||||
if (cpu_flag_memory) vm::passive_lock(*this);
|
||||
break;
|
||||
}
|
||||
else if (!cpu_sleep_called)
|
||||
{
|
||||
cpu_sleep();
|
||||
cpu_sleep_called = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -140,16 +149,21 @@ bool cpu_thread::check_state()
|
||||
return false;
|
||||
}
|
||||
|
||||
void cpu_thread::test_state()
|
||||
{
|
||||
if (UNLIKELY(test(state)))
|
||||
{
|
||||
if (check_state())
|
||||
{
|
||||
throw cpu_flag::ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_thread::run()
|
||||
{
|
||||
state -= cpu_flag::stop;
|
||||
lock_notify();
|
||||
}
|
||||
|
||||
void cpu_thread::set_signal()
|
||||
{
|
||||
verify("cpu_flag::signal" HERE), !state.test_and_set(cpu_flag::signal);
|
||||
notify(); // TODO: lock_notify?
|
||||
notify();
|
||||
}
|
||||
|
||||
std::string cpu_thread::dump() const
|
||||
|
||||
@@ -8,9 +8,10 @@ enum class cpu_flag : u32
|
||||
{
|
||||
stop, // Thread not running (HLE, initial state)
|
||||
exit, // Irreversible exit
|
||||
suspend, // Thread paused
|
||||
suspend, // Thread suspended
|
||||
ret, // Callback return requested
|
||||
signal, // Thread received a signal (HLE)
|
||||
memory, // Thread must unlock memory mutex
|
||||
|
||||
dbg_global_pause, // Emulation paused
|
||||
dbg_global_stop, // Emulation stopped
|
||||
@@ -31,30 +32,39 @@ public:
|
||||
virtual void on_stop() override;
|
||||
virtual ~cpu_thread() override;
|
||||
|
||||
const id_value<> id{};
|
||||
const u32 id;
|
||||
|
||||
cpu_thread();
|
||||
cpu_thread(u32 id);
|
||||
|
||||
// Public thread state
|
||||
atomic_t<bs_t<cpu_flag>> state{+cpu_flag::stop};
|
||||
|
||||
// Object associated with sleep state, possibly synchronization primitive (mutex, semaphore, etc.)
|
||||
atomic_t<void*> owner{};
|
||||
|
||||
// Process thread state, return true if the checker must return
|
||||
bool check_state();
|
||||
|
||||
// Process thread state
|
||||
void test_state();
|
||||
|
||||
// Run thread
|
||||
void run();
|
||||
|
||||
// Set cpu_flag::signal
|
||||
void set_signal();
|
||||
// Check thread type
|
||||
u32 id_type()
|
||||
{
|
||||
return id >> 24;
|
||||
}
|
||||
|
||||
// Thread stats for external observation
|
||||
static atomic_t<u64> g_threads_created, g_threads_deleted;
|
||||
|
||||
// Print CPU state
|
||||
virtual std::string dump() const;
|
||||
|
||||
// Thread entry point function
|
||||
virtual void cpu_task() = 0;
|
||||
|
||||
// Callback for cpu_flag::suspend
|
||||
virtual void cpu_sleep() {}
|
||||
};
|
||||
|
||||
inline cpu_thread* get_current_cpu_thread() noexcept
|
||||
|
||||
+37
-33
@@ -28,6 +28,7 @@ enum CellError : u32
|
||||
CELL_ETIMEDOUT = 0x8001000B, // The operation is timed out
|
||||
CELL_EABORT = 0x8001000C, // The operation is aborted
|
||||
CELL_EFAULT = 0x8001000D, // Invalid memory access
|
||||
CELL_ENOCHILD = 0x8001000E, // Process has no child(s)
|
||||
CELL_ESTAT = 0x8001000F, // State of the target thread is invalid
|
||||
CELL_EALIGN = 0x80010010, // Alignment is invalid.
|
||||
CELL_EKRESOURCE = 0x80010011, // Shortage of the kernel resources
|
||||
@@ -40,37 +41,40 @@ enum CellError : u32
|
||||
CELL_ENOTMSELF = 0x80010018, // The file is not a MSELF
|
||||
CELL_ESYSVER = 0x80010019, // System version error
|
||||
CELL_EAUTHFATAL = 0x8001001A, // Fatal system error
|
||||
CELL_EDOM = 0x8001001B,
|
||||
CELL_ERANGE = 0x8001001C,
|
||||
CELL_EILSEQ = 0x8001001D,
|
||||
CELL_EFPOS = 0x8001001E,
|
||||
CELL_EINTR = 0x8001001F,
|
||||
CELL_EFBIG = 0x80010020,
|
||||
CELL_EMLINK = 0x80010021,
|
||||
CELL_ENFILE = 0x80010022,
|
||||
CELL_ENOSPC = 0x80010023,
|
||||
CELL_ENOTTY = 0x80010024,
|
||||
CELL_EPIPE = 0x80010025,
|
||||
CELL_EROFS = 0x80010026,
|
||||
CELL_ESPIPE = 0x80010027,
|
||||
CELL_E2BIG = 0x80010028,
|
||||
CELL_EACCES = 0x80010029,
|
||||
CELL_EBADF = 0x8001002A,
|
||||
CELL_EIO = 0x8001002B,
|
||||
CELL_EMFILE = 0x8001002C,
|
||||
CELL_ENODEV = 0x8001002D,
|
||||
CELL_ENOTDIR = 0x8001002E,
|
||||
CELL_ENXIO = 0x8001002F,
|
||||
CELL_EXDEV = 0x80010030,
|
||||
CELL_EBADMSG = 0x80010031,
|
||||
CELL_EINPROGRESS = 0x80010032,
|
||||
CELL_EMSGSIZE = 0x80010033,
|
||||
CELL_ENAMETOOLONG = 0x80010034,
|
||||
CELL_ENOLCK = 0x80010035,
|
||||
CELL_ENOTEMPTY = 0x80010036,
|
||||
CELL_ENOTSUP = 0x80010037,
|
||||
CELL_EFSSPECIFIC = 0x80010038,
|
||||
CELL_EOVERFLOW = 0x80010039,
|
||||
CELL_ENOTMOUNTED = 0x8001003A,
|
||||
CELL_ENOTSDATA = 0x8001003B,
|
||||
CELL_EDOM = 0x8001001B, // Math domain violation
|
||||
CELL_ERANGE = 0x8001001C, // Math range violation
|
||||
CELL_EILSEQ = 0x8001001D, // Illegal multi-byte sequence in input
|
||||
CELL_EFPOS = 0x8001001E, // File position error
|
||||
CELL_EINTR = 0x8001001F, // Syscall was interrupted
|
||||
CELL_EFBIG = 0x80010020, // File too large
|
||||
CELL_EMLINK = 0x80010021, // Too many links
|
||||
CELL_ENFILE = 0x80010022, // File table overflow
|
||||
CELL_ENOSPC = 0x80010023, // No space left on device
|
||||
CELL_ENOTTY = 0x80010024, // Not a TTY
|
||||
CELL_EPIPE = 0x80010025, // Broken pipe
|
||||
CELL_EROFS = 0x80010026, // Read-only filesystem (write fail)
|
||||
CELL_ESPIPE = 0x80010027, // Illegal seek (e.g. seek on pipe)
|
||||
CELL_E2BIG = 0x80010028, // Arg list too long
|
||||
CELL_EACCES = 0x80010029, // Access violation
|
||||
CELL_EBADF = 0x8001002A, // Invalid file descriptor
|
||||
CELL_EIO = 0x8001002B, // Filesystem mounting failed (actually IO error...EIO)
|
||||
CELL_EMFILE = 0x8001002C, // Too many files open
|
||||
CELL_ENODEV = 0x8001002D, // No device
|
||||
CELL_ENOTDIR = 0x8001002E, // Not a directory
|
||||
CELL_ENXIO = 0x8001002F, // No such device or IO
|
||||
CELL_EXDEV = 0x80010030, // Cross-device link error
|
||||
CELL_EBADMSG = 0x80010031, // Bad Message
|
||||
CELL_EINPROGRESS = 0x80010032, // In progress
|
||||
CELL_EMSGSIZE = 0x80010033, // Message size error
|
||||
CELL_ENAMETOOLONG = 0x80010034, // Name too long
|
||||
CELL_ENOLCK = 0x80010035, // No lock
|
||||
CELL_ENOTEMPTY = 0x80010036, // Not empty
|
||||
CELL_ENOTSUP = 0x80010037, // Not supported
|
||||
CELL_EFSSPECIFIC = 0x80010038, // File-system specific error
|
||||
CELL_EOVERFLOW = 0x80010039, // Overflow occured
|
||||
CELL_ENOTMOUNTED = 0x8001003A, // Filesystem not mounted
|
||||
CELL_ENOTSDATA = 0x8001003B, // Not SData
|
||||
CELL_ESDKVER = 0x8001003C, // Incorrect version in sys_load_param
|
||||
CELL_ENOLICDISC = 0x8001003D, // Pointer is null. Similar than 0x8001003E but with some PARAM.SFO parameter (TITLE_ID?) embedded.
|
||||
CELL_ENOLICENT = 0x8001003E, // Pointer is null
|
||||
};
|
||||
|
||||
+311
-37
@@ -1,47 +1,321 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "Emu/Cell/lv2/sys_sync.h"
|
||||
#include "MFC.h"
|
||||
|
||||
const char* get_mfc_cmd_name(u32 cmd)
|
||||
template <>
|
||||
void fmt_class_string<MFC>::format(std::string& out, u64 arg)
|
||||
{
|
||||
switch (cmd)
|
||||
format_enum(out, arg, [](MFC cmd)
|
||||
{
|
||||
case MFC_PUT_CMD: return "PUT";
|
||||
case MFC_PUTB_CMD: return "PUTB";
|
||||
case MFC_PUTF_CMD: return "PUTF";
|
||||
case MFC_PUTS_CMD: return "PUTS";
|
||||
case MFC_PUTBS_CMD: return "PUTBS";
|
||||
case MFC_PUTFS_CMD: return "PUTFS";
|
||||
case MFC_PUTR_CMD: return "PUTR";
|
||||
case MFC_PUTRB_CMD: return "PUTRB";
|
||||
case MFC_PUTRF_CMD: return "PUTRF";
|
||||
case MFC_GET_CMD: return "GET";
|
||||
case MFC_GETB_CMD: return "GETB";
|
||||
case MFC_GETF_CMD: return "GETF";
|
||||
case MFC_GETS_CMD: return "GETS";
|
||||
case MFC_GETBS_CMD: return "GETBS";
|
||||
case MFC_GETFS_CMD: return "GETFS";
|
||||
case MFC_PUTL_CMD: return "PUTL";
|
||||
case MFC_PUTLB_CMD: return "PUTLB";
|
||||
case MFC_PUTLF_CMD: return "PUTLF";
|
||||
case MFC_PUTRL_CMD: return "PUTRL";
|
||||
case MFC_PUTRLB_CMD: return "PUTRLB";
|
||||
case MFC_PUTRLF_CMD: return "PUTRLF";
|
||||
case MFC_GETL_CMD: return "GETL";
|
||||
case MFC_GETLB_CMD: return "GETLB";
|
||||
case MFC_GETLF_CMD: return "GETLF";
|
||||
switch (cmd)
|
||||
{
|
||||
case MFC_PUT_CMD: return "PUT";
|
||||
case MFC_PUTB_CMD: return "PUTB";
|
||||
case MFC_PUTF_CMD: return "PUTF";
|
||||
case MFC_PUTS_CMD: return "PUTS";
|
||||
case MFC_PUTBS_CMD: return "PUTBS";
|
||||
case MFC_PUTFS_CMD: return "PUTFS";
|
||||
case MFC_PUTR_CMD: return "PUTR";
|
||||
case MFC_PUTRB_CMD: return "PUTRB";
|
||||
case MFC_PUTRF_CMD: return "PUTRF";
|
||||
case MFC_GET_CMD: return "GET";
|
||||
case MFC_GETB_CMD: return "GETB";
|
||||
case MFC_GETF_CMD: return "GETF";
|
||||
case MFC_GETS_CMD: return "GETS";
|
||||
case MFC_GETBS_CMD: return "GETBS";
|
||||
case MFC_GETFS_CMD: return "GETFS";
|
||||
case MFC_PUTL_CMD: return "PUTL";
|
||||
case MFC_PUTLB_CMD: return "PUTLB";
|
||||
case MFC_PUTLF_CMD: return "PUTLF";
|
||||
case MFC_PUTRL_CMD: return "PUTRL";
|
||||
case MFC_PUTRLB_CMD: return "PUTRLB";
|
||||
case MFC_PUTRLF_CMD: return "PUTRLF";
|
||||
case MFC_GETL_CMD: return "GETL";
|
||||
case MFC_GETLB_CMD: return "GETLB";
|
||||
case MFC_GETLF_CMD: return "GETLF";
|
||||
|
||||
case MFC_GETLLAR_CMD: return "GETLLAR";
|
||||
case MFC_PUTLLC_CMD: return "PUTLLC";
|
||||
case MFC_PUTLLUC_CMD: return "PUTLLUC";
|
||||
case MFC_PUTQLLUC_CMD: return "PUTQLLUC";
|
||||
case MFC_GETLLAR_CMD: return "GETLLAR";
|
||||
case MFC_PUTLLC_CMD: return "PUTLLC";
|
||||
case MFC_PUTLLUC_CMD: return "PUTLLUC";
|
||||
case MFC_PUTQLLUC_CMD: return "PUTQLLUC";
|
||||
|
||||
case MFC_SNDSIG_CMD: return "SNDSIG";
|
||||
case MFC_SNDSIGB_CMD: return "SNDSIGB";
|
||||
case MFC_SNDSIGF_CMD: return "SNDSIGF";
|
||||
case MFC_BARRIER_CMD: return "BARRIER";
|
||||
case MFC_EIEIO_CMD: return "EIEIO";
|
||||
case MFC_SYNC_CMD: return "SYNC";
|
||||
case MFC_SNDSIG_CMD: return "SNDSIG";
|
||||
case MFC_SNDSIGB_CMD: return "SNDSIGB";
|
||||
case MFC_SNDSIGF_CMD: return "SNDSIGF";
|
||||
case MFC_BARRIER_CMD: return "BARRIER";
|
||||
case MFC_EIEIO_CMD: return "EIEIO";
|
||||
case MFC_SYNC_CMD: return "SYNC";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
mfc_thread::mfc_thread()
|
||||
: cpu_thread(0)
|
||||
{
|
||||
}
|
||||
|
||||
mfc_thread::~mfc_thread()
|
||||
{
|
||||
}
|
||||
|
||||
std::string mfc_thread::get_name() const
|
||||
{
|
||||
return "MFC Thread";
|
||||
}
|
||||
|
||||
void mfc_thread::cpu_task()
|
||||
{
|
||||
vm::passive_lock(*this);
|
||||
|
||||
u32 no_updates = 0;
|
||||
|
||||
while (!m_spus.empty() || m_spuq.size() != 0)
|
||||
{
|
||||
// Add or remove destroyed SPU threads
|
||||
while (m_spuq.size())
|
||||
{
|
||||
auto& thread_ptr = m_spuq[0];
|
||||
|
||||
// Look for deleted threads if nullptr received
|
||||
for (auto it = m_spus.cbegin(); !thread_ptr && it != m_spus.cend();)
|
||||
{
|
||||
if (test(it->get()->state, cpu_flag::exit))
|
||||
{
|
||||
it = m_spus.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// Add thread
|
||||
if (thread_ptr)
|
||||
{
|
||||
m_spus.emplace_back(std::move(thread_ptr));
|
||||
}
|
||||
|
||||
m_spuq.end_pop();
|
||||
no_updates = 0;
|
||||
}
|
||||
|
||||
test_state();
|
||||
|
||||
// Process SPU threads
|
||||
for (const auto& thread_ptr : m_spus)
|
||||
{
|
||||
SPUThread& spu = *thread_ptr;
|
||||
|
||||
const auto proxy_size = spu.mfc_proxy.size();
|
||||
const auto queue_size = spu.mfc_queue.size();
|
||||
|
||||
if (proxy_size)
|
||||
{
|
||||
const auto& cmd = spu.mfc_proxy[0];
|
||||
|
||||
spu.do_dma_transfer(cmd);
|
||||
|
||||
if (cmd.cmd & MFC_START_MASK && !spu.status.test_and_set(SPU_STATUS_RUNNING))
|
||||
{
|
||||
spu.run();
|
||||
}
|
||||
|
||||
spu.mfc_proxy.end_pop();
|
||||
no_updates = 0;
|
||||
}
|
||||
|
||||
test_state();
|
||||
|
||||
if (queue_size)
|
||||
{
|
||||
auto& cmd = spu.mfc_queue[0];
|
||||
|
||||
if ((cmd.cmd & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK)) == MFC_PUTQLLUC_CMD)
|
||||
{
|
||||
auto& data = vm::ps3::_ref<decltype(spu.rdata)>(cmd.eal);
|
||||
const auto to_write = spu._ref<decltype(spu.rdata)>(cmd.lsa & 0x3ffff);
|
||||
|
||||
cmd.size = 0;
|
||||
no_updates = 0;
|
||||
|
||||
// Store unconditionally
|
||||
vm::writer_lock lock(0);
|
||||
data = to_write;
|
||||
vm::reservation_update(cmd.eal, 128);
|
||||
vm::notify(cmd.eal, 128);
|
||||
}
|
||||
else if (cmd.cmd & MFC_LIST_MASK)
|
||||
{
|
||||
struct list_element
|
||||
{
|
||||
be_t<u16> sb; // Stall-and-Notify bit (0x8000)
|
||||
be_t<u16> ts; // List Transfer Size
|
||||
be_t<u32> ea; // External Address Low
|
||||
};
|
||||
|
||||
if (cmd.size && (spu.ch_stall_mask & (1u << cmd.tag)) == 0)
|
||||
{
|
||||
cmd.lsa &= 0x3fff0;
|
||||
|
||||
const list_element item = spu._ref<list_element>(cmd.eal & 0x3fff8);
|
||||
|
||||
const u32 size = item.ts;
|
||||
const u32 addr = item.ea;
|
||||
|
||||
if (size)
|
||||
{
|
||||
spu_mfc_cmd transfer;
|
||||
transfer.eal = addr;
|
||||
transfer.eah = 0;
|
||||
transfer.lsa = cmd.lsa | (addr & 0xf);
|
||||
transfer.tag = cmd.tag;
|
||||
transfer.cmd = MFC(cmd.cmd & ~MFC_LIST_MASK);
|
||||
transfer.size = size;
|
||||
|
||||
spu.do_dma_transfer(transfer);
|
||||
cmd.lsa += std::max<u32>(size, 16);
|
||||
}
|
||||
|
||||
cmd.eal += 8;
|
||||
cmd.size -= 8;
|
||||
no_updates = 0;
|
||||
|
||||
if (item.sb & 0x8000)
|
||||
{
|
||||
spu.ch_stall_stat.push_or(spu, 1 << cmd.tag);
|
||||
|
||||
const u32 evt = spu.ch_event_stat.fetch_or(SPU_EVENT_SN);
|
||||
|
||||
if (evt & SPU_EVENT_WAITING)
|
||||
{
|
||||
spu.notify();
|
||||
}
|
||||
else if (evt & SPU_EVENT_INTR_ENABLED)
|
||||
{
|
||||
spu.state += cpu_flag::suspend;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (LIKELY(cmd.size))
|
||||
{
|
||||
spu.do_dma_transfer(cmd);
|
||||
cmd.size = 0;
|
||||
}
|
||||
else if (UNLIKELY((cmd.cmd & ~0xc) == MFC_BARRIER_CMD))
|
||||
{
|
||||
// TODO (MFC_BARRIER_CMD, MFC_EIEIO_CMD, MFC_SYNC_CMD)
|
||||
_mm_mfence();
|
||||
}
|
||||
|
||||
if (!cmd.size)
|
||||
{
|
||||
spu.mfc_queue.end_pop();
|
||||
no_updates = 0;
|
||||
}
|
||||
}
|
||||
|
||||
test_state();
|
||||
|
||||
if (spu.ch_tag_upd)
|
||||
{
|
||||
// Mask incomplete transfers
|
||||
u32 completed = spu.ch_tag_mask;
|
||||
|
||||
for (u32 i = 0; i < spu.mfc_queue.size(); i++)
|
||||
{
|
||||
const auto& _cmd = spu.mfc_queue[i];
|
||||
|
||||
if (_cmd.size)
|
||||
{
|
||||
if (spu.ch_tag_upd == 1)
|
||||
{
|
||||
completed &= ~(1u << _cmd.tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
completed = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (completed && spu.ch_tag_upd.exchange(0))
|
||||
{
|
||||
spu.ch_tag_stat.push(spu, completed);
|
||||
no_updates = 0;
|
||||
}
|
||||
}
|
||||
|
||||
test_state();
|
||||
}
|
||||
|
||||
if (no_updates++)
|
||||
{
|
||||
if (no_updates >= 3)
|
||||
{
|
||||
if (m_spuq.size())
|
||||
{
|
||||
no_updates = 0;
|
||||
}
|
||||
|
||||
for (const auto& thread_ptr : m_spus)
|
||||
{
|
||||
SPUThread& spu = *thread_ptr;
|
||||
|
||||
if (spu.mfc_proxy.size())
|
||||
{
|
||||
no_updates = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (spu.mfc_queue.size())
|
||||
{
|
||||
auto& cmd = spu.mfc_queue[0];
|
||||
|
||||
if ((cmd.cmd & MFC_LIST_MASK) == 0 || (spu.ch_stall_mask & (1u << cmd.tag)) == 0)
|
||||
{
|
||||
no_updates = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (spu.ch_tag_upd)
|
||||
{
|
||||
no_updates = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (no_updates)
|
||||
{
|
||||
vm::temporary_unlock(*this);
|
||||
thread_ctrl::wait_for(100);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vm::reader_lock lock;
|
||||
vm::notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
vm::passive_unlock(*this);
|
||||
state += cpu_flag::stop;
|
||||
}
|
||||
|
||||
void mfc_thread::add_spu(spu_ptr _spu)
|
||||
{
|
||||
while (!m_spuq.try_push(std::move(_spu)))
|
||||
{
|
||||
busy_wait();
|
||||
continue;
|
||||
}
|
||||
|
||||
run();
|
||||
}
|
||||
|
||||
+50
-26
@@ -1,8 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
const char* get_mfc_cmd_name(u32 cmd);
|
||||
#include "Emu/CPU/CPUThread.h"
|
||||
#include "Utilities/lockless.h"
|
||||
|
||||
enum : u32
|
||||
#include <deque>
|
||||
|
||||
enum MFC : u8
|
||||
{
|
||||
MFC_PUT_CMD = 0x20, MFC_PUTB_CMD = 0x21, MFC_PUTF_CMD = 0x22,
|
||||
MFC_PUTS_CMD = 0x28, MFC_PUTBS_CMD = 0x29, MFC_PUTFS_CMD = 0x2a,
|
||||
@@ -64,29 +67,50 @@ enum : u32
|
||||
MFC_SPU_MAX_QUEUE_SPACE = 0x10,
|
||||
};
|
||||
|
||||
struct spu_mfc_arg_t
|
||||
enum : u32
|
||||
{
|
||||
union
|
||||
{
|
||||
u64 ea;
|
||||
|
||||
struct
|
||||
{
|
||||
u32 eal;
|
||||
u32 eah;
|
||||
};
|
||||
};
|
||||
|
||||
u32 lsa;
|
||||
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u16 tag;
|
||||
u16 size;
|
||||
};
|
||||
|
||||
u32 size_tag;
|
||||
};
|
||||
MFC_DMA_TAG_STATUS_UPDATE_EVENT = 0x00000001,
|
||||
MFC_DMA_TAG_CMD_STALL_NOTIFY_EVENT = 0x00000002,
|
||||
MFC_DMA_QUEUE_VACANCY_EVENT = 0x00000008,
|
||||
MFC_SPU_MAILBOX_WRITTEN_EVENT = 0x00000010,
|
||||
MFC_DECREMENTER_EVENT = 0x00000020,
|
||||
MFC_PU_INT_MAILBOX_AVAIL_EVENT = 0x00000040,
|
||||
MFC_PU_MAILBOX_AVAIL_EVENT = 0x00000080,
|
||||
MFC_SIGNAL_2_EVENT = 0x00000100,
|
||||
MFC_SIGNAL_1_EVENT = 0x00000200,
|
||||
MFC_LLR_LOST_EVENT = 0x00000400,
|
||||
MFC_PRIV_ATTN_EVENT = 0x00000800,
|
||||
MFG_MULTISOURCE_SYNC_EVENT = 0x00001000,
|
||||
};
|
||||
|
||||
struct alignas(16) spu_mfc_cmd
|
||||
{
|
||||
MFC cmd;
|
||||
u8 tag;
|
||||
u16 size;
|
||||
u32 lsa;
|
||||
u32 eal;
|
||||
u32 eah;
|
||||
};
|
||||
|
||||
class mfc_thread : public cpu_thread
|
||||
{
|
||||
using spu_ptr = std::shared_ptr<class SPUThread>;
|
||||
|
||||
// SPU threads to poll
|
||||
std::vector<spu_ptr> m_spus;
|
||||
|
||||
// SPU threads to enqueue
|
||||
lf_mpsc<spu_ptr, 128> m_spuq;
|
||||
|
||||
public:
|
||||
mfc_thread();
|
||||
|
||||
virtual ~mfc_thread() override;
|
||||
|
||||
virtual std::string get_name() const override;
|
||||
|
||||
virtual void cpu_task() override;
|
||||
|
||||
virtual void add_spu(spu_ptr _spu);
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/Cell/lv2/sys_sync.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@@ -199,6 +200,7 @@ public:
|
||||
// TODO: finalize
|
||||
cellAdec.warning("adecEndSeq:");
|
||||
cbFunc(*this, id, CELL_ADEC_MSG_TYPE_SEQDONE, CELL_OK, cbArg);
|
||||
lv2_obj::sleep(*this);
|
||||
|
||||
just_finished = true;
|
||||
break;
|
||||
@@ -375,11 +377,13 @@ public:
|
||||
{
|
||||
frame.data = nullptr; // to prevent destruction
|
||||
cbFunc(*this, id, CELL_ADEC_MSG_TYPE_PCMOUT, CELL_OK, cbArg);
|
||||
lv2_obj::sleep(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cbFunc(*this, id, CELL_ADEC_MSG_TYPE_AUDONE, task.au.auInfo_addr, cbArg);
|
||||
lv2_obj::sleep(*this);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -608,9 +612,7 @@ s32 cellAdecClose(u32 handle)
|
||||
|
||||
while (!adec->is_finished)
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
|
||||
std::this_thread::sleep_for(1ms); // hack
|
||||
thread_ctrl::wait_for(1000); // hack
|
||||
}
|
||||
|
||||
idm::remove<ppu_thread>(handle);
|
||||
@@ -905,6 +907,30 @@ s32 cellAdecGetPcmItem(u32 handle, vm::pptr<CellAdecPcmItem> pcmItem)
|
||||
|
||||
DECLARE(ppu_module_manager::cellAdec)("cellAdec", []()
|
||||
{
|
||||
static ppu_static_module cell_libac3dec("cell_libac3dec");
|
||||
static ppu_static_module cellAtrac3dec("cellAtrac3dec");
|
||||
static ppu_static_module cellAtracXdec("cellAtracXdec");
|
||||
static ppu_static_module cellCelpDec("cellCelpDec");
|
||||
static ppu_static_module cellDTSdec("cellDTSdec");
|
||||
static ppu_static_module cellM2AACdec("cellM2AACdec");
|
||||
static ppu_static_module cellM2BCdec("cellM2BCdec");
|
||||
static ppu_static_module cellM4AacDec("cellM4AacDec");
|
||||
static ppu_static_module cellMP3dec("cellMP3dec");
|
||||
static ppu_static_module cellTRHDdec("cellTRHDdec");
|
||||
static ppu_static_module cellWMAdec("cellWMAdec");
|
||||
static ppu_static_module cellDTSLBRdec("cellDTSLBRdec");
|
||||
static ppu_static_module cellDDPdec("cellDDPdec");
|
||||
static ppu_static_module cellM4AacDec2ch("cellM4AacDec2ch");
|
||||
static ppu_static_module cellDTSHDdec("cellDTSHDdec");
|
||||
static ppu_static_module cellMPL1dec("cellMPL1dec");
|
||||
static ppu_static_module cellMP3Sdec("cellMP3Sdec");
|
||||
static ppu_static_module cellM4AacDec2chmod("cellM4AacDec2chmod");
|
||||
static ppu_static_module cellCelp8Dec("cellCelp8Dec");
|
||||
static ppu_static_module cellWMAPROdec("cellWMAPROdec");
|
||||
static ppu_static_module cellWMALSLdec("cellWMALSLdec");
|
||||
static ppu_static_module cellDTSHDCOREdec("cellDTSHDCOREdec");
|
||||
static ppu_static_module cellAtrac3multidec("cellAtrac3multidec");
|
||||
|
||||
REG_FUNC(cellAdec, cellAdecQueryAttr);
|
||||
REG_FUNC(cellAdec, cellAdecOpen);
|
||||
REG_FUNC(cellAdec, cellAdecOpenEx);
|
||||
|
||||
@@ -16,8 +16,11 @@ logs::channel cellAudio("cellAudio", logs::level::notice);
|
||||
cfg::bool_entry g_cfg_audio_dump_to_file(cfg::root.audio, "Dump to file");
|
||||
cfg::bool_entry g_cfg_audio_convert_to_u16(cfg::root.audio, "Convert to 16 bit");
|
||||
|
||||
void audio_config::on_task()
|
||||
void audio_config::on_init(const std::shared_ptr<void>& _this)
|
||||
{
|
||||
m_buffer.set(vm::alloc(AUDIO_PORT_OFFSET * AUDIO_PORT_COUNT, vm::main));
|
||||
m_indexes.set(vm::alloc(sizeof(u64) * AUDIO_PORT_COUNT, vm::main));
|
||||
|
||||
for (u32 i = 0; i < AUDIO_PORT_COUNT; i++)
|
||||
{
|
||||
ports[i].number = i;
|
||||
@@ -25,6 +28,11 @@ void audio_config::on_task()
|
||||
ports[i].index = m_indexes + i;
|
||||
}
|
||||
|
||||
named_thread::on_init(_this);
|
||||
}
|
||||
|
||||
void audio_config::on_task()
|
||||
{
|
||||
AudioDumper m_dump(g_cfg_audio_dump_to_file ? 2 : 0); // Init AudioDumper for 2 channels if enabled
|
||||
|
||||
float buf2ch[2 * BUFFER_SIZE]{}; // intermediate buffer for 2 channels
|
||||
@@ -281,14 +289,13 @@ void audio_config::on_task()
|
||||
|
||||
// send aftermix event (normal audio event)
|
||||
|
||||
LV2_LOCK;
|
||||
semaphore_lock lock(mutex);
|
||||
|
||||
for (u64 key : keys)
|
||||
{
|
||||
if (auto&& queue = lv2_event_queue_t::find(key))
|
||||
if (auto queue = lv2_event_queue::find(key))
|
||||
{
|
||||
if (queue->events() < queue->size)
|
||||
queue->push(lv2_lock, 0, 0, 0, 0); // TODO: check arguments
|
||||
queue->send(0, 0, 0, 0); // TODO: check arguments
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -658,16 +665,26 @@ s32 cellAudioCreateNotifyEventQueue(vm::ptr<u32> id, vm::ptr<u64> key)
|
||||
{
|
||||
cellAudio.warning("cellAudioCreateNotifyEventQueue(id=*0x%x, key=*0x%x)", id, key);
|
||||
|
||||
for (u64 k = 0; k < 100; k++)
|
||||
vm::var<sys_event_queue_attribute_t> attr;
|
||||
attr->protocol = SYS_SYNC_FIFO;
|
||||
attr->type = SYS_PPU_QUEUE;
|
||||
attr->name_u64 = 0;
|
||||
|
||||
for (u64 i = 0; i < 100; i++)
|
||||
{
|
||||
const u64 key_value = 0x80004d494f323221ull + k;
|
||||
|
||||
// Create an event queue "bruteforcing" an available key
|
||||
if (auto&& queue = lv2_event_queue_t::make(SYS_SYNC_FIFO, SYS_PPU_QUEUE, 0, key_value, 32))
|
||||
{
|
||||
*id = queue->id;
|
||||
*key = key_value;
|
||||
const u64 key_value = 0x80004d494f323221ull + i;
|
||||
|
||||
if (const s32 res = sys_event_queue_create(id, attr, key_value, 32))
|
||||
{
|
||||
if (res != CELL_EEXIST)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*key = key_value;
|
||||
return CELL_OK;
|
||||
}
|
||||
}
|
||||
@@ -700,7 +717,7 @@ s32 cellAudioSetNotifyEventQueue(u64 key)
|
||||
return CELL_AUDIO_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
LV2_LOCK;
|
||||
semaphore_lock lock(g_audio->mutex);
|
||||
|
||||
for (auto k : g_audio->keys) // check for duplicates
|
||||
{
|
||||
@@ -735,7 +752,7 @@ s32 cellAudioRemoveNotifyEventQueue(u64 key)
|
||||
return CELL_AUDIO_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
LV2_LOCK;
|
||||
semaphore_lock lock(g_audio->mutex);
|
||||
|
||||
for (auto i = g_audio->keys.begin(); i != g_audio->keys.end(); i++)
|
||||
{
|
||||
|
||||
@@ -125,19 +125,29 @@ class audio_config final : public named_thread
|
||||
|
||||
std::string get_name() const override { return "Audio Thread"; }
|
||||
|
||||
vm::var<char[], vm::page_allocator<vm::main>> m_buffer{ AUDIO_PORT_OFFSET * AUDIO_PORT_COUNT };
|
||||
vm::var<u64[], vm::page_allocator<vm::main>> m_indexes{ AUDIO_PORT_COUNT };
|
||||
vm::ptr<char> m_buffer = vm::null;
|
||||
vm::ptr<u64> m_indexes = vm::null;
|
||||
|
||||
u64 m_counter{};
|
||||
|
||||
public:
|
||||
void on_init(const std::shared_ptr<void>&) override;
|
||||
|
||||
const u64 start_time = get_system_time();
|
||||
|
||||
std::array<audio_port, AUDIO_PORT_COUNT> ports;
|
||||
|
||||
std::vector<u64> keys;
|
||||
|
||||
~audio_config() noexcept = default;
|
||||
semaphore<> mutex;
|
||||
|
||||
audio_config() = default;
|
||||
|
||||
~audio_config()
|
||||
{
|
||||
vm::dealloc_verbose_nothrow(m_buffer.addr());
|
||||
vm::dealloc_verbose_nothrow(m_indexes.addr());
|
||||
}
|
||||
|
||||
audio_port* open_port()
|
||||
{
|
||||
|
||||
@@ -132,11 +132,11 @@ s32 cellVideoOutGetScreenSize(u32 videoOut, vm::ptr<float> screenSize)
|
||||
|
||||
DECLARE(ppu_module_manager::cellAvconfExt)("cellSysutilAvconfExt", []()
|
||||
{
|
||||
REG_VNID(cellSysutilAvconfExt, 0x00000000, g_gamma, []
|
||||
REG_VNID(cellSysutilAvconfExt, 0x00000000, g_gamma).init = []
|
||||
{
|
||||
// Test
|
||||
*g_gamma = 1.0f;
|
||||
});
|
||||
};
|
||||
|
||||
REG_FUNC(cellSysutilAvconfExt, cellAudioOutUnregisterDevice);
|
||||
REG_FUNC(cellSysutilAvconfExt, cellAudioOutGetDeviceInfo2);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/Cell/lv2/sys_sync.h"
|
||||
|
||||
#include "cellPamf.h"
|
||||
#include "cellDmux.h"
|
||||
@@ -143,10 +144,14 @@ class ElementaryStream
|
||||
bool is_full(u32 space);
|
||||
|
||||
public:
|
||||
static const u32 id_base = 1;
|
||||
static const u32 id_step = 1;
|
||||
static const u32 id_count = 1023;
|
||||
|
||||
ElementaryStream(Demuxer* dmux, u32 addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, vm::ptr<CellDmuxCbEsMsg> cbFunc, u32 cbArg, u32 spec);
|
||||
|
||||
Demuxer* dmux;
|
||||
const id_value<> id{};
|
||||
const u32 id = idm::last_id();
|
||||
const u32 memAddr;
|
||||
const u32 memSize;
|
||||
const u32 fidMajor;
|
||||
@@ -238,6 +243,7 @@ public:
|
||||
dmuxMsg->msgType = CELL_DMUX_MSG_TYPE_DEMUX_DONE;
|
||||
dmuxMsg->supplementalInfo = stream.userdata;
|
||||
cbFunc(*this, id, dmuxMsg, cbArg);
|
||||
lv2_obj::sleep(*this);
|
||||
|
||||
is_working = false;
|
||||
|
||||
@@ -391,6 +397,7 @@ public:
|
||||
esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND;
|
||||
esMsg->supplementalInfo = stream.userdata;
|
||||
es.cbFunc(*this, id, es.id, esMsg, es.cbArg);
|
||||
lv2_obj::sleep(*this);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -456,6 +463,7 @@ public:
|
||||
esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND;
|
||||
esMsg->supplementalInfo = stream.userdata;
|
||||
es.cbFunc(*this, id, es.id, esMsg, es.cbArg);
|
||||
lv2_obj::sleep(*this);
|
||||
}
|
||||
|
||||
if (pes.has_ts)
|
||||
@@ -532,6 +540,7 @@ public:
|
||||
dmuxMsg->msgType = CELL_DMUX_MSG_TYPE_DEMUX_DONE;
|
||||
dmuxMsg->supplementalInfo = stream.userdata;
|
||||
cbFunc(*this, id, dmuxMsg, cbArg);
|
||||
lv2_obj::sleep(*this);
|
||||
|
||||
stream = {};
|
||||
|
||||
@@ -620,6 +629,7 @@ public:
|
||||
esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND;
|
||||
esMsg->supplementalInfo = stream.userdata;
|
||||
es.cbFunc(*this, id, es.id, esMsg, es.cbArg);
|
||||
lv2_obj::sleep(*this);
|
||||
}
|
||||
|
||||
if (es.raw_data.size())
|
||||
@@ -632,6 +642,7 @@ public:
|
||||
esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_FLUSH_DONE;
|
||||
esMsg->supplementalInfo = stream.userdata;
|
||||
es.cbFunc(*this, id, es.id, esMsg, es.cbArg);
|
||||
lv2_obj::sleep(*this);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1362,6 +1373,8 @@ s32 cellDmuxFlushEs(u32 esHandle)
|
||||
|
||||
DECLARE(ppu_module_manager::cellDmux)("cellDmux", []()
|
||||
{
|
||||
static ppu_static_module cellDmuxPamf("cellDmuxPamf");
|
||||
|
||||
REG_FUNC(cellDmux, cellDmuxQueryAttr);
|
||||
REG_FUNC(cellDmux, cellDmuxQueryAttr2);
|
||||
REG_FUNC(cellDmux, cellDmuxOpen);
|
||||
|
||||
@@ -743,6 +743,8 @@ s32 cellFontGraphicsGetLineRGBA()
|
||||
|
||||
DECLARE(ppu_module_manager::cellFont)("cellFont", []()
|
||||
{
|
||||
static ppu_static_module cell_FreeType2("cell_FreeType2");
|
||||
|
||||
REG_FUNC(cellFont, cellFontSetFontsetOpenMode);
|
||||
REG_FUNC(cellFont, cellFontSetFontOpenMode);
|
||||
REG_FUNC(cellFont, cellFontCreateRenderer);
|
||||
|
||||
+102
-171
@@ -4,12 +4,15 @@
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "Emu/Cell/lv2/sys_fs.h"
|
||||
#include "Emu/Cell/lv2/sys_sync.h"
|
||||
#include "cellFs.h"
|
||||
|
||||
#include "Utilities/StrUtil.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace vm { using namespace ps3; }
|
||||
|
||||
logs::channel cellFs("cellFs", logs::level::notice);
|
||||
|
||||
s32 cellFsOpen(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, vm::cptr<void> arg, u64 size)
|
||||
@@ -206,6 +209,16 @@ s32 cellFsChmod(vm::cptr<char> path, s32 mode)
|
||||
return sys_fs_chmod(path, mode);
|
||||
}
|
||||
|
||||
s32 cellFsUtime(vm::cptr<char> path, vm::cptr<CellFsUtimbuf> timep)
|
||||
{
|
||||
cellFs.warning("cellFsUtime(path=%s, timep=*0x%x) -> sys_fs_utime()", path, timep);
|
||||
|
||||
// TODO
|
||||
|
||||
// Call the syscall
|
||||
return sys_fs_utime(path, timep);
|
||||
}
|
||||
|
||||
s32 cellFsGetFreeSize(vm::cptr<char> path, vm::ptr<u32> block_size, vm::ptr<u64> block_count)
|
||||
{
|
||||
cellFs.warning("cellFsGetFreeSize(path=%s, block_size=*0x%x, block_count=*0x%x)", path, block_size, block_count);
|
||||
@@ -221,7 +234,7 @@ s32 cellFsGetDirectoryEntries(u32 fd, vm::ptr<CellFsDirectoryEntry> entries, u32
|
||||
{
|
||||
cellFs.warning("cellFsGetDirectoryEntries(fd=%d, entries=*0x%x, entries_size=0x%x, data_count=*0x%x)", fd, entries, entries_size, data_count);
|
||||
|
||||
const auto directory = idm::get<lv2_dir>(fd);
|
||||
const auto directory = idm::get<lv2_fs_object, lv2_dir>(fd);
|
||||
|
||||
if (!directory)
|
||||
{
|
||||
@@ -345,7 +358,7 @@ s32 cellFsStReadInit(u32 fd, vm::cptr<CellFsRingBuffer> ringbuf)
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
const auto file = idm::get<lv2_file>(fd);
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -366,7 +379,7 @@ s32 cellFsStReadFinish(u32 fd)
|
||||
{
|
||||
cellFs.todo("cellFsStReadFinish(fd=%d)", fd);
|
||||
|
||||
const auto file = idm::get<lv2_file>(fd);
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -382,7 +395,7 @@ s32 cellFsStReadGetRingBuf(u32 fd, vm::ptr<CellFsRingBuffer> ringbuf)
|
||||
{
|
||||
cellFs.todo("cellFsStReadGetRingBuf(fd=%d, ringbuf=*0x%x)", fd, ringbuf);
|
||||
|
||||
const auto file = idm::get<lv2_file>(fd);
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -398,7 +411,7 @@ s32 cellFsStReadGetStatus(u32 fd, vm::ptr<u64> status)
|
||||
{
|
||||
cellFs.todo("cellFsStReadGetRingBuf(fd=%d, status=*0x%x)", fd, status);
|
||||
|
||||
const auto file = idm::get<lv2_file>(fd);
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -414,7 +427,7 @@ s32 cellFsStReadGetRegid(u32 fd, vm::ptr<u64> regid)
|
||||
{
|
||||
cellFs.todo("cellFsStReadGetRingBuf(fd=%d, regid=*0x%x)", fd, regid);
|
||||
|
||||
const auto file = idm::get<lv2_file>(fd);
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -430,7 +443,7 @@ s32 cellFsStReadStart(u32 fd, u64 offset, u64 size)
|
||||
{
|
||||
cellFs.todo("cellFsStReadStart(fd=%d, offset=0x%llx, size=0x%llx)", fd, offset, size);
|
||||
|
||||
const auto file = idm::get<lv2_file>(fd);
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -446,7 +459,7 @@ s32 cellFsStReadStop(u32 fd)
|
||||
{
|
||||
cellFs.todo("cellFsStReadStop(fd=%d)", fd);
|
||||
|
||||
const auto file = idm::get<lv2_file>(fd);
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -462,7 +475,7 @@ s32 cellFsStRead(u32 fd, vm::ptr<u8> buf, u64 size, vm::ptr<u64> rsize)
|
||||
{
|
||||
cellFs.todo("cellFsStRead(fd=%d, buf=*0x%x, size=0x%llx, rsize=*0x%x)", fd, buf, size, rsize);
|
||||
|
||||
const auto file = idm::get<lv2_file>(fd);
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -478,7 +491,7 @@ s32 cellFsStReadGetCurrentAddr(u32 fd, vm::ptr<u32> addr, vm::ptr<u64> size)
|
||||
{
|
||||
cellFs.todo("cellFsStReadGetCurrentAddr(fd=%d, addr=*0x%x, size=*0x%x)", fd, addr, size);
|
||||
|
||||
const auto file = idm::get<lv2_file>(fd);
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -494,7 +507,7 @@ s32 cellFsStReadPutCurrentAddr(u32 fd, vm::ptr<u8> addr, u64 size)
|
||||
{
|
||||
cellFs.todo("cellFsStReadPutCurrentAddr(fd=%d, addr=*0x%x, size=0x%llx)", fd, addr, size);
|
||||
|
||||
const auto file = idm::get<lv2_file>(fd);
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -510,7 +523,7 @@ s32 cellFsStReadWait(u32 fd, u64 size)
|
||||
{
|
||||
cellFs.todo("cellFsStReadWait(fd=%d, size=0x%llx)", fd, size);
|
||||
|
||||
const auto file = idm::get<lv2_file>(fd);
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -526,7 +539,7 @@ s32 cellFsStReadWaitCallback(u32 fd, u64 size, vm::ptr<void(s32 xfd, u64 xsize)>
|
||||
{
|
||||
cellFs.todo("cellFsStReadWaitCallback(fd=%d, size=0x%llx, func=*0x%x)", fd, size, func);
|
||||
|
||||
const auto file = idm::get<lv2_file>(fd);
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -538,113 +551,6 @@ s32 cellFsStReadWaitCallback(u32 fd, u64 size, vm::ptr<void(s32 xfd, u64 xsize)>
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
bool sdata_check(u32 version, u32 flags, u64 filesizeInput, u64 filesizeTmp)
|
||||
{
|
||||
if (version > 4 || flags & 0x7EFFFFC0){
|
||||
printf("ERROR: unknown version");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((version == 1 && (flags & 0x7FFFFFFE)) ||
|
||||
(version == 2 && (flags & 0x7EFFFFC0))){
|
||||
printf("ERROR: unknown or unsupported type");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filesizeTmp > filesizeInput){
|
||||
printf("ERROR: input file size is too short.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(flags & 0x80000000)){
|
||||
printf("ERROR: cannot extract finalized edata.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
s32 sdata_unpack(const std::string& packed_file, const std::string& unpacked_file)
|
||||
{
|
||||
fs::file packed_stream(vfs::get(packed_file));
|
||||
fs::file unpacked_stream(vfs::get(unpacked_file), fs::rewrite);
|
||||
|
||||
if (!packed_stream)
|
||||
{
|
||||
cellFs.error("File '%s' not found!", packed_file);
|
||||
return CELL_ENOENT;
|
||||
}
|
||||
|
||||
if (!unpacked_stream)
|
||||
{
|
||||
cellFs.error("File '%s' couldn't be created!", unpacked_file);
|
||||
return CELL_ENOENT;
|
||||
}
|
||||
|
||||
char buffer[10200];
|
||||
packed_stream.read(buffer, 256);
|
||||
u32 format = *(be_t<u32>*)&buffer[0];
|
||||
if (format != 0x4E504400) // "NPD\x00"
|
||||
{
|
||||
cellFs.error("Illegal format. Expected 0x4E504400, but got 0x%08x", format);
|
||||
return CELL_EFSSPECIFIC;
|
||||
}
|
||||
|
||||
u32 version = *(be_t<u32>*)&buffer[0x04];
|
||||
u32 flags = *(be_t<u32>*)&buffer[0x80];
|
||||
u32 blockSize = *(be_t<u32>*)&buffer[0x84];
|
||||
u64 filesizeOutput = *(be_t<u64>*)&buffer[0x88];
|
||||
u64 filesizeInput = packed_stream.size();
|
||||
u32 blockCount = (u32)((filesizeOutput + blockSize - 1) / blockSize);
|
||||
|
||||
// SDATA file is compressed
|
||||
if (flags & 0x1)
|
||||
{
|
||||
cellFs.warning("cellFsSdataOpen: Compressed SDATA files are not supported yet.");
|
||||
return CELL_EFSSPECIFIC;
|
||||
}
|
||||
// SDATA file is NOT compressed
|
||||
else
|
||||
{
|
||||
u32 t1 = (flags & 0x20) ? 0x20 : 0x10;
|
||||
u32 startOffset = (blockCount * t1) + 0x100;
|
||||
u64 filesizeTmp = (filesizeOutput + 0xF) & 0xFFFFFFF0 + startOffset;
|
||||
|
||||
if (!sdata_check(version, flags, filesizeInput, filesizeTmp))
|
||||
{
|
||||
cellFs.error("cellFsSdataOpen: Wrong header information.");
|
||||
return CELL_EFSSPECIFIC;
|
||||
}
|
||||
|
||||
if (flags & 0x20)
|
||||
{
|
||||
packed_stream.seek(0x100);
|
||||
}
|
||||
else
|
||||
{
|
||||
packed_stream.seek(startOffset);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < blockCount; i++)
|
||||
{
|
||||
if (flags & 0x20)
|
||||
{
|
||||
packed_stream.seek(t1, fs::seek_cur);
|
||||
}
|
||||
|
||||
if (!(blockCount - i - 1))
|
||||
{
|
||||
blockSize = (u32)(filesizeOutput - i * blockSize);
|
||||
}
|
||||
|
||||
packed_stream.read(buffer + 256, blockSize);
|
||||
unpacked_stream.write(buffer + 256, blockSize);
|
||||
}
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsSdataOpen(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, vm::cptr<void> arg, u64 size)
|
||||
{
|
||||
cellFs.notice("cellFsSdataOpen(path=%s, flags=%#o, fd=*0x%x, arg=*0x%x, size=0x%llx)", path, flags, fd, arg, size);
|
||||
@@ -654,33 +560,52 @@ s32 cellFsSdataOpen(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, vm::cptr<vo
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
return cellFsOpen(path, CELL_FS_O_RDONLY, fd, vm::make_var<be_t<u32>[2]>({ 0x180, 0x10 }), 8);
|
||||
|
||||
// Don't implement sdata decryption in this function, it should be done in sys_fs_open() syscall or somewhere else
|
||||
|
||||
/*
|
||||
std::string suffix = path.substr(path.length() - 5, 5);
|
||||
if (suffix != ".sdat" && suffix != ".SDAT")
|
||||
return CELL_ENOTSDATA;
|
||||
|
||||
std::string::size_type last_slash = path.rfind('/'); //TODO: use a filesystem library to solve this more robustly
|
||||
last_slash = last_slash == std::string::npos ? 0 : last_slash+1;
|
||||
std::string unpacked_path = "/dev_hdd1/"+path.substr(last_slash,path.length()-last_slash)+".unpacked";
|
||||
s32 ret = sdata_unpack(path, unpacked_path);
|
||||
if (ret) return ret;
|
||||
|
||||
fd = idm::GetNewID(Emu.GetVFS().OpenFile(unpacked_path, vfsRead), TYPE_FS_FILE);
|
||||
|
||||
return CELL_OK;
|
||||
*/
|
||||
return cellFsOpen(path, CELL_FS_O_RDONLY, fd, vm::make_var<be_t<u32>[2]>({0x180, 0x10}), 8);
|
||||
}
|
||||
|
||||
s32 cellFsSdataOpenByFd(u32 mself_fd, s32 flags, vm::ptr<u32> sdata_fd, u64 offset, vm::cptr<void> arg, u64 size)
|
||||
{
|
||||
cellFs.todo("cellFsSdataOpenByFd(mself_fd=0x%x, flags=%#o, sdata_fd=*0x%x, offset=0x%llx, arg=*0x%x, size=0x%llx)", mself_fd, flags, sdata_fd, offset, arg, size);
|
||||
cellFs.notice("cellFsSdataOpenByFd(mself_fd=0x%x, flags=%#o, sdata_fd=*0x%x, offset=0x%llx, arg=*0x%x, size=0x%llx)", mself_fd, flags, sdata_fd, offset, arg, size);
|
||||
|
||||
// TODO:
|
||||
if (!sdata_fd)
|
||||
{
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
*sdata_fd = -1;
|
||||
|
||||
if (mself_fd < 3 || mself_fd > 255)
|
||||
{
|
||||
return CELL_EBADF;
|
||||
}
|
||||
|
||||
if (flags)
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
vm::var<lv2_file_op_09> ctrl;
|
||||
ctrl->_vtable = vm::cast(0xfa880000); // Intentionally wrong (provide correct vtable if necessary)
|
||||
ctrl->op = 0x80000009;
|
||||
ctrl->fd = mself_fd;
|
||||
ctrl->offset = offset;
|
||||
ctrl->_vtabl2 = vm::cast(0xfa880020);
|
||||
ctrl->arg1 = 0x180;
|
||||
ctrl->arg2 = 0x10;
|
||||
ctrl->arg_ptr = arg.addr();
|
||||
ctrl->arg_size = u32(size);
|
||||
|
||||
if (const s32 rc = sys_fs_fcntl(mself_fd, 0x80000009, ctrl, 0x40))
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (const s32 rc = ctrl->out_code)
|
||||
{
|
||||
return rc;
|
||||
}
|
||||
|
||||
*sdata_fd = ctrl->out_fd;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -710,7 +635,7 @@ struct fs_aio_thread : ppu_thread
|
||||
s32 error = CELL_OK;
|
||||
u64 result = 0;
|
||||
|
||||
const auto file = idm::get<lv2_file>(aio->fd);
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(aio->fd);
|
||||
|
||||
if (!file || (type == 1 && file->flags & CELL_FS_O_WRONLY) || (type == 2 && !(file->flags & CELL_FS_O_ACCMODE)))
|
||||
{
|
||||
@@ -730,6 +655,7 @@ struct fs_aio_thread : ppu_thread
|
||||
}
|
||||
|
||||
func(*this, aio, error, xid, result);
|
||||
lv2_obj::sleep(*this);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -737,12 +663,6 @@ struct fs_aio_thread : ppu_thread
|
||||
struct fs_aio_manager
|
||||
{
|
||||
std::shared_ptr<fs_aio_thread> thread;
|
||||
|
||||
fs_aio_manager()
|
||||
: thread(idm::make_ptr<ppu_thread, fs_aio_thread>("FS AIO Thread", 500))
|
||||
{
|
||||
thread->run();
|
||||
}
|
||||
};
|
||||
|
||||
s32 cellFsAioInit(vm::cptr<char> mount_point)
|
||||
@@ -750,7 +670,13 @@ s32 cellFsAioInit(vm::cptr<char> mount_point)
|
||||
cellFs.warning("cellFsAioInit(mount_point=%s)", mount_point);
|
||||
|
||||
// TODO: create AIO thread (if not exists) for specified mount point
|
||||
fxm::get_always<fs_aio_manager>();
|
||||
const auto m = fxm::make<fs_aio_manager>();
|
||||
|
||||
if (m)
|
||||
{
|
||||
m->thread = idm::make_ptr<ppu_thread, fs_aio_thread>("FS AIO Thread", 500);
|
||||
m->thread->run();
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -772,9 +698,14 @@ s32 cellFsAioRead(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
|
||||
|
||||
// TODO: detect mount point and send AIO request to the AIO thread of this mount point
|
||||
|
||||
const s32 xid = (*id = ++g_fs_aio_id);
|
||||
const auto m = fxm::get<fs_aio_manager>();
|
||||
|
||||
const auto m = fxm::get_always<fs_aio_manager>();
|
||||
if (!m)
|
||||
{
|
||||
return CELL_ENXIO;
|
||||
}
|
||||
|
||||
const s32 xid = (*id = ++g_fs_aio_id);
|
||||
|
||||
m->thread->cmd_list
|
||||
({
|
||||
@@ -782,7 +713,7 @@ s32 cellFsAioRead(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
|
||||
{ aio, func },
|
||||
});
|
||||
|
||||
m->thread->lock_notify();
|
||||
m->thread->notify();
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -793,9 +724,14 @@ s32 cellFsAioWrite(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
|
||||
|
||||
// TODO: detect mount point and send AIO request to the AIO thread of this mount point
|
||||
|
||||
const s32 xid = (*id = ++g_fs_aio_id);
|
||||
const auto m = fxm::get<fs_aio_manager>();
|
||||
|
||||
const auto m = fxm::get_always<fs_aio_manager>();
|
||||
if (!m)
|
||||
{
|
||||
return CELL_ENXIO;
|
||||
}
|
||||
|
||||
const s32 xid = (*id = ++g_fs_aio_id);
|
||||
|
||||
m->thread->cmd_list
|
||||
({
|
||||
@@ -803,7 +739,7 @@ s32 cellFsAioWrite(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
|
||||
{ aio, func },
|
||||
});
|
||||
|
||||
m->thread->lock_notify();
|
||||
m->thread->notify();
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -828,7 +764,7 @@ s32 cellFsSetIoBufferFromDefaultContainer(u32 fd, u32 buffer_size, u32 page_type
|
||||
{
|
||||
cellFs.todo("cellFsSetIoBufferFromDefaultContainer(fd=%d, buffer_size=%d, page_type=%d)", fd, buffer_size, page_type);
|
||||
|
||||
const auto file = idm::get<lv2_file>(fd);
|
||||
const auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
@@ -838,11 +774,6 @@ s32 cellFsSetIoBufferFromDefaultContainer(u32 fd, u32 buffer_size, u32 page_type
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsUtime()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
}
|
||||
|
||||
s32 cellFsArcadeHddSerialNumber()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
@@ -911,24 +842,24 @@ DECLARE(ppu_module_manager::cellFs)("sys_fs", []()
|
||||
REG_FUNC(sys_fs, cellFsOpen);
|
||||
REG_FUNC(sys_fs, cellFsSdataOpen);
|
||||
REG_FUNC(sys_fs, cellFsSdataOpenByFd);
|
||||
REG_FUNC(sys_fs, cellFsRead, MFF_PERFECT);
|
||||
REG_FUNC(sys_fs, cellFsWrite, MFF_PERFECT);
|
||||
REG_FUNC(sys_fs, cellFsClose, MFF_PERFECT);
|
||||
REG_FUNC(sys_fs, cellFsRead).flags = MFF_PERFECT;
|
||||
REG_FUNC(sys_fs, cellFsWrite).flags = MFF_PERFECT;
|
||||
REG_FUNC(sys_fs, cellFsClose).flags = MFF_PERFECT;
|
||||
REG_FUNC(sys_fs, cellFsOpendir);
|
||||
REG_FUNC(sys_fs, cellFsReaddir, MFF_PERFECT);
|
||||
REG_FUNC(sys_fs, cellFsClosedir, MFF_PERFECT);
|
||||
REG_FUNC(sys_fs, cellFsReaddir).flags = MFF_PERFECT;
|
||||
REG_FUNC(sys_fs, cellFsClosedir).flags = MFF_PERFECT;
|
||||
REG_FUNC(sys_fs, cellFsStat);
|
||||
REG_FUNC(sys_fs, cellFsFstat, MFF_PERFECT);
|
||||
REG_FUNC(sys_fs, cellFsFstat).flags = MFF_PERFECT;
|
||||
REG_FUNC(sys_fs, cellFsMkdir);
|
||||
REG_FUNC(sys_fs, cellFsRename);
|
||||
REG_FUNC(sys_fs, cellFsChmod);
|
||||
REG_FUNC(sys_fs, cellFsFsync);
|
||||
REG_FUNC(sys_fs, cellFsRmdir);
|
||||
REG_FUNC(sys_fs, cellFsUnlink);
|
||||
REG_FUNC(sys_fs, cellFsLseek, MFF_PERFECT);
|
||||
REG_FUNC(sys_fs, cellFsFtruncate, MFF_PERFECT);
|
||||
REG_FUNC(sys_fs, cellFsLseek).flags = MFF_PERFECT;
|
||||
REG_FUNC(sys_fs, cellFsFtruncate).flags = MFF_PERFECT;
|
||||
REG_FUNC(sys_fs, cellFsTruncate);
|
||||
REG_FUNC(sys_fs, cellFsFGetBlockSize, MFF_PERFECT);
|
||||
REG_FUNC(sys_fs, cellFsFGetBlockSize).flags = MFF_PERFECT;
|
||||
REG_FUNC(sys_fs, cellFsAioInit);
|
||||
REG_FUNC(sys_fs, cellFsAioFinish);
|
||||
REG_FUNC(sys_fs, cellFsAioRead);
|
||||
@@ -961,7 +892,7 @@ DECLARE(ppu_module_manager::cellFs)("sys_fs", []()
|
||||
REG_FUNC(sys_fs, cellFsAllocateFileAreaByFdWithInitialData);
|
||||
REG_FUNC(sys_fs, cellFsTruncate2);
|
||||
REG_FUNC(sys_fs, cellFsChangeFileSizeWithoutAllocation);
|
||||
REG_FUNC(sys_fs, cellFsAllocateFileAreaWithoutZeroFill, MFF_FORCED_HLE);
|
||||
REG_FUNC(sys_fs, cellFsAllocateFileAreaWithoutZeroFill).flags = MFF_FORCED_HLE;
|
||||
REG_FUNC(sys_fs, cellFsChangeFileSizeByFdWithoutAllocation);
|
||||
REG_FUNC(sys_fs, cellFsSetDiscReadRetrySetting);
|
||||
REG_FUNC(sys_fs, cellFsRegisterConversionCallback);
|
||||
|
||||
@@ -179,9 +179,12 @@ s32 cellHddGameCheck2()
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
}
|
||||
|
||||
s32 cellHddGameGetSizeKB()
|
||||
s32 cellHddGameGetSizeKB(vm::ptr<u32> size)
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
cellGame.todo("cellHddGameGetSizeKB(size=*0x%x)", size);
|
||||
|
||||
*size = 0;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellHddGameSetSystemVer()
|
||||
@@ -197,11 +200,9 @@ s32 cellHddGameExitBroken()
|
||||
|
||||
s32 cellGameDataGetSizeKB(vm::ptr<u32> size)
|
||||
{
|
||||
cellGame.warning("cellGameDataGetSizeKB(): Unimplemented, returning 0");
|
||||
if (size)
|
||||
{
|
||||
*size = 0;
|
||||
}
|
||||
cellGame.todo("cellGameDataGetSizeKB(size=*0x%x)", size);
|
||||
|
||||
*size = 0;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -246,7 +247,7 @@ error_code cellGameBootCheck(vm::ptr<u32> type, vm::ptr<u32> attributes, vm::ptr
|
||||
return CELL_GAME_ERROR_BUSY;
|
||||
}
|
||||
}
|
||||
else if (category == "HG")
|
||||
else if (category == "AP" || category == "AV" || category == "HG")
|
||||
{
|
||||
*type = CELL_GAME_GAMETYPE_HDD;
|
||||
*attributes = 0; // TODO
|
||||
@@ -404,6 +405,8 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
|
||||
{
|
||||
cellGame.error("cellGameDataCheckCreate2(version=0x%x, dirName=%s, errDialog=0x%x, funcStat=*0x%x, container=%d)", version, dirName, errDialog, funcStat, container);
|
||||
|
||||
//older sdk. it might not care about game type.
|
||||
|
||||
if (version != CELL_GAMEDATA_VERSION_CURRENT || errDialog > 1)
|
||||
{
|
||||
return CELL_GAMEDATA_ERROR_PARAM;
|
||||
@@ -413,21 +416,15 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
|
||||
|
||||
const std::string& dir = "/dev_hdd0/game/"s + dirName.get_ptr();
|
||||
|
||||
if (!fs::is_dir(vfs::get(dir)))
|
||||
{
|
||||
cellGame.todo("cellGameDataCheckCreate2(): should create directory '%s'", dir);
|
||||
// TODO: create data
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
vm::var<CellGameDataCBResult> cbResult;
|
||||
vm::var<CellGameDataStatGet> cbGet;
|
||||
vm::var<CellGameDataStatSet> cbSet;
|
||||
cbGet->isNewData = fs::is_dir(vfs::get(dir)) ? CELL_GAMEDATA_ISNEWDATA_NO : CELL_GAMEDATA_ISNEWDATA_YES;
|
||||
|
||||
// TODO: Use the free space of the computer's HDD where RPCS3 is being run.
|
||||
cbGet->hddFreeSizeKB = 40000000; //40 GB
|
||||
|
||||
cbGet->isNewData = CELL_GAMEDATA_ISNEWDATA_NO;
|
||||
|
||||
strcpy_trunc(cbGet->contentInfoPath, dir);
|
||||
strcpy_trunc(cbGet->gameDataPath, dir + "/USRDIR");
|
||||
|
||||
@@ -447,23 +444,55 @@ error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char>
|
||||
strcpy_trunc(cbGet->getParam.dataVersion, psf::get_string(sfo, "APP_VER", ""));
|
||||
strcpy_trunc(cbGet->getParam.titleId, psf::get_string(sfo, "TITLE_ID", ""));
|
||||
strcpy_trunc(cbGet->getParam.title, psf::get_string(sfo, "TITLE", ""));
|
||||
// TODO: write lang titles
|
||||
for (u32 i = 0; i < CELL_HDDGAME_SYSP_LANGUAGE_NUM; i++)
|
||||
{
|
||||
strcpy_trunc(cbGet->getParam.titleLang[i], psf::get_string(sfo, fmt::format("TITLE_%02d", i)));
|
||||
}
|
||||
|
||||
|
||||
funcStat(ppu, cbResult, cbGet, cbSet);
|
||||
|
||||
if (cbSet->setParam)
|
||||
{
|
||||
// TODO: write PARAM.SFO from cbSet
|
||||
cellGame.todo("cellGameDataCheckCreate2(): writing PARAM.SFO parameters (addr=0x%x)", cbSet->setParam);
|
||||
}
|
||||
|
||||
|
||||
switch ((s32)cbResult->result)
|
||||
{
|
||||
case CELL_GAMEDATA_CBRESULT_OK_CANCEL:
|
||||
// TODO: do not process game data
|
||||
// TODO: do not process game data(directory)
|
||||
cellGame.warning("cellGameDataCheckCreate2(): callback returned CELL_GAMEDATA_CBRESULT_OK_CANCEL");
|
||||
return CELL_OK;
|
||||
|
||||
case CELL_GAMEDATA_CBRESULT_OK:
|
||||
|
||||
case CELL_GAMEDATA_CBRESULT_OK:
|
||||
//game confirmed that it wants to create directory
|
||||
if (!fs::is_dir(vfs::get(dir + "/USRDIR")) && !fs::create_path(vfs::get(dir + "/USRDIR")))
|
||||
{
|
||||
cellGame.error("cellGameDataCheckCreate2(): folder creation failed");
|
||||
return CELL_GAME_ERROR_NOSPACE; //don't know which error. picked one at random
|
||||
}
|
||||
if (cbSet->setParam)
|
||||
{
|
||||
const auto vdir = vfs::get(dir);
|
||||
|
||||
//older SDK does not define not settable values, hopefully it doesn't just change some values(overwrite)
|
||||
psf::registry sfo_write =
|
||||
{
|
||||
{ "TITLE_ID", psf::string(CELL_GAME_SYSP_TITLEID_SIZE, cbSet->setParam->titleId) },
|
||||
{ "TITLE", psf::string(CELL_GAME_SYSP_TITLE_SIZE, cbSet->setParam->title) },
|
||||
{ "VERSION", psf::string(CELL_GAME_SYSP_VERSION_SIZE, cbSet->setParam->dataVersion) },
|
||||
{ "PARENTAL_LEVEL", cbSet->setParam->parentalLevel.value() }
|
||||
};
|
||||
//sfo_write.emplace("PARENTAL_LEVEL", cbSet->setParam->parentalLevel.value()); // I don't care about age restrictions.
|
||||
for (u32 i = 0; i < CELL_HDDGAME_SYSP_LANGUAGE_NUM; i++)
|
||||
{
|
||||
sfo_write.emplace(fmt::format("TITLE_%02d", i), psf::string(CELL_GAME_SYSP_TITLE_SIZE, cbSet->setParam->titleLang[i]));
|
||||
}
|
||||
if (!fs::is_dir(vdir))
|
||||
{
|
||||
cellGame.fatal("directory where param.sfo is to be created does not exist");
|
||||
return CELL_GAME_ERROR_INTERNAL;
|
||||
}
|
||||
psf::save_object(fs::file(vdir + "/PARAM.SFO", fs::rewrite), sfo_write);
|
||||
|
||||
}
|
||||
return CELL_OK;
|
||||
|
||||
case CELL_GAMEDATA_CBRESULT_ERR_NOSPACE: // TODO: process errors, error message and needSizeKB result
|
||||
@@ -600,6 +629,7 @@ static const char* get_param_string_key(s32 id)
|
||||
case CELL_GAME_PARAMID_TITLE_POLISH: return "TITLE_16";
|
||||
case CELL_GAME_PARAMID_TITLE_PORTUGUESE_BRAZIL: return "TITLE_17";
|
||||
case CELL_GAME_PARAMID_TITLE_ENGLISH_UK: return "TITLE_18";
|
||||
case CELL_GAME_PARAMID_TITLE_TURKISH: return "TITLE_19";
|
||||
|
||||
case CELL_GAME_PARAMID_TITLE_ID: return "TITLE_ID";
|
||||
case CELL_GAME_PARAMID_VERSION: return "VERSION";
|
||||
@@ -669,9 +699,11 @@ error_code cellGameSetParamString(s32 id, vm::cptr<char> buf)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellGameGetSizeKB()
|
||||
s32 cellGameGetSizeKB(vm::ptr<s32> size)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGame);
|
||||
cellGame.todo("cellGameGetSizeKB(size=*0x%x)", size);
|
||||
|
||||
*size = 0;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -735,8 +767,7 @@ error_code cellGameContentErrorDialog(s32 type, s32 errNeedSizeKB, vm::cptr<char
|
||||
|
||||
while (!result)
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
std::this_thread::sleep_for(1ms);
|
||||
thread_ctrl::wait_for(1000);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
@@ -755,9 +786,27 @@ s32 cellGameThemeInstallFromBuffer()
|
||||
}
|
||||
|
||||
|
||||
s32 cellDiscGameGetBootDiscInfo()
|
||||
s32 cellDiscGameGetBootDiscInfo(vm::ptr<CellDiscGameSystemFileParam> getParam)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGame);
|
||||
cellGame.warning("cellDiscGameGetBootDiscInfo(getParam=*0x%x)", getParam);
|
||||
|
||||
// This is also called by non-disc games, see NPUB90029
|
||||
const std::string dir = "/dev_bdvd/PS3_GAME"s;
|
||||
|
||||
if (!fs::is_dir(vfs::get(dir)))
|
||||
{
|
||||
// Not a disc game. TODO: Fetch PARAM.SFO from proper game dir
|
||||
cellGame.warning("cellDiscGameGetBootDiscInfo(): directory '%s' not found", dir);
|
||||
getParam->parentalLevel = 0;
|
||||
strcpy_trunc(getParam->titleId, "0");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
const auto& psf = psf::load_object(fs::file(vfs::get(dir + "/PARAM.SFO")));
|
||||
|
||||
if (psf.count("PARENTAL_LEVEL") != 0) getParam->parentalLevel = psf.at("PARENTAL_LEVEL").as_integer();
|
||||
if (psf.count("TITLE_ID") != 0) strcpy_trunc(getParam->titleId, psf.at("TITLE_ID").as_string());
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -104,8 +104,9 @@ enum
|
||||
CELL_GAME_PARAMID_TITLE_DANISH = 16,
|
||||
CELL_GAME_PARAMID_TITLE_NORWEGIAN = 17,
|
||||
CELL_GAME_PARAMID_TITLE_POLISH = 18,
|
||||
CELL_GAME_PARAMID_TITLE_PORTUGUESE_BRAZIL = 19,
|
||||
CELL_GAME_PARAMID_TITLE_ENGLISH_UK = 20,
|
||||
CELL_GAME_PARAMID_TITLE_PORTUGUESE_BRAZIL = 19, // FW 4.00
|
||||
CELL_GAME_PARAMID_TITLE_ENGLISH_UK = 20, // FW 4.00
|
||||
CELL_GAME_PARAMID_TITLE_TURKISH = 21, // FW 4.30
|
||||
CELL_GAME_PARAMID_TITLE_ID = 100,
|
||||
CELL_GAME_PARAMID_VERSION = 101,
|
||||
CELL_GAME_PARAMID_PS3_SYSTEM_VER = 105,
|
||||
@@ -178,6 +179,8 @@ enum // old consts
|
||||
|
||||
CELL_GAMEDATA_ERRDIALOG_NONE = 0,
|
||||
CELL_GAMEDATA_ERRDIALOG_ALWAYS = 1,
|
||||
|
||||
CELL_DISCGAME_SYSP_TITLEID_SIZE=10,
|
||||
};
|
||||
|
||||
struct CellGameDataSystemFileParam
|
||||
@@ -192,6 +195,12 @@ struct CellGameDataSystemFileParam
|
||||
be_t<u32> attribute;
|
||||
char reserved2[256];
|
||||
};
|
||||
struct CellDiscGameSystemFileParam {
|
||||
char titleId[CELL_DISCGAME_SYSP_TITLEID_SIZE];
|
||||
char reserved0[2];
|
||||
be_t<u32> parentalLevel;
|
||||
char reserved1[16];
|
||||
};
|
||||
|
||||
struct CellGameDataStatGet
|
||||
{
|
||||
|
||||
@@ -29,14 +29,17 @@ s32 cellGameGetHomeDataImportPath()
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
}
|
||||
|
||||
s32 cellGameGetHomeLaunchOptionPath()
|
||||
s32 cellGameGetHomeLaunchOptionPath(vm::ptr<char> commonPath, vm::ptr<char> personalPath)
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
cellGameExec.todo("cellGameGetHomeLaunchOptionPath(commonPath=%s, personalPath=%s)", commonPath, personalPath);
|
||||
|
||||
// TODO: PlayStation home is not supported atm.
|
||||
return CELL_GAME_ERROR_NOAPP;
|
||||
}
|
||||
|
||||
s32 cellGameGetBootGameInfo(vm::ptr<u32> type, vm::ptr<char> dirName, vm::ptr<u32> execData)
|
||||
{
|
||||
cellGameExec.todo("cellGameGetBootGameInfo(type=*0x%x, dirName=*0x%x, execData=*0x%x)", type, dirName, execData);
|
||||
cellGameExec.todo("cellGameGetBootGameInfo(type=*0x%x, dirName=%s, execData=*0x%x)", type, dirName, execData);
|
||||
|
||||
// TODO: Support more boot types
|
||||
*type = CELL_GAME_GAMETYPE_SYS;
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
logs::channel cellGcmSys("cellGcmSys", logs::level::notice);
|
||||
|
||||
extern s32 cellGcmCallback(vm::ptr<CellGcmContextData> context, u32 count);
|
||||
extern void ppu_register_function_at(u32 addr, ppu_function_t ptr);
|
||||
extern s32 cellGcmCallback(ppu_thread& ppu, vm::ptr<CellGcmContextData> context, u32 count);
|
||||
extern void ppu_register_function_at(u32 addr, u32 size, ppu_function_t ptr);
|
||||
|
||||
const u32 tiled_pitches[] = {
|
||||
0x00000000, 0x00000200, 0x00000300, 0x00000400,
|
||||
@@ -222,13 +222,13 @@ u32 cellGcmGetControlRegister()
|
||||
u32 cellGcmGetDefaultCommandWordSize()
|
||||
{
|
||||
cellGcmSys.trace("cellGcmGetDefaultCommandWordSize()");
|
||||
return 0x400;
|
||||
return gcm_info.command_size;
|
||||
}
|
||||
|
||||
u32 cellGcmGetDefaultSegmentWordSize()
|
||||
{
|
||||
cellGcmSys.trace("cellGcmGetDefaultSegmentWordSize()");
|
||||
return 0x100;
|
||||
return gcm_info.segment_size;
|
||||
}
|
||||
|
||||
s32 cellGcmInitDefaultFifoMode(s32 mode)
|
||||
@@ -385,7 +385,7 @@ s32 _cellGcmInitBody(vm::pptr<CellGcmContextData> context, u32 cmdSize, u32 ioSi
|
||||
vm::write32(gcm_info.context_addr + 0x44, 0xabadcafe);
|
||||
vm::write32(gcm_info.context_addr + 0x48, ppu_instructions::HACK(FIND_FUNC(cellGcmCallback)));
|
||||
vm::write32(gcm_info.context_addr + 0x4c, ppu_instructions::BLR());
|
||||
ppu_register_function_at(gcm_info.context_addr + 0x48, BIND_FUNC(cellGcmCallback));
|
||||
ppu_register_function_at(gcm_info.context_addr + 0x48, 8, BIND_FUNC(cellGcmCallback));
|
||||
|
||||
vm::_ref<CellGcmContextData>(gcm_info.context_addr) = current_context;
|
||||
context->set(gcm_info.context_addr);
|
||||
@@ -1134,9 +1134,22 @@ void cellGcmSetDefaultCommandBuffer()
|
||||
vm::write32(fxm::get<GSRender>()->ctxt_addr, gcm_info.context_addr);
|
||||
}
|
||||
|
||||
s32 cellGcmSetDefaultCommandBufferAndSegmentWordSize()
|
||||
s32 cellGcmSetDefaultCommandBufferAndSegmentWordSize(u32 bufferSize, u32 segmentSize)
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
cellGcmSys.warning("cellGcmSetDefaultCommandBufferAndSegmentWordSize(bufferSize = 0x%x, segmentSize = 0x%x)", bufferSize, segmentSize);
|
||||
|
||||
const auto& put = vm::_ref<CellGcmControl>(gcm_info.control_addr).put;
|
||||
const auto& get = vm::_ref<CellGcmControl>(gcm_info.control_addr).get;
|
||||
|
||||
if (put != 0x1000 || get != 0x1000 || bufferSize < segmentSize * 2)
|
||||
{
|
||||
return CELL_GCM_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
gcm_info.command_size = bufferSize;
|
||||
gcm_info.segment_size = segmentSize;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
@@ -1285,12 +1298,12 @@ static bool isInCommandBufferExcept(u32 getPos, u32 bufferBegin, u32 bufferEnd)
|
||||
return true;
|
||||
}
|
||||
|
||||
s32 cellGcmCallback(vm::ptr<CellGcmContextData> context, u32 count)
|
||||
s32 cellGcmCallback(ppu_thread& ppu, vm::ptr<CellGcmContextData> context, u32 count)
|
||||
{
|
||||
cellGcmSys.trace("cellGcmCallback(context=*0x%x, count=0x%x)", context, count);
|
||||
|
||||
auto& ctrl = vm::_ref<CellGcmControl>(gcm_info.control_addr);
|
||||
const std::chrono::time_point<std::chrono::system_clock> enterWait = std::chrono::system_clock::now();
|
||||
|
||||
// Flush command buffer (ie allow RSX to read up to context->current)
|
||||
ctrl.put.exchange(getOffsetFromAddress(context->current.addr()));
|
||||
|
||||
@@ -1304,16 +1317,14 @@ s32 cellGcmCallback(vm::ptr<CellGcmContextData> context, u32 count)
|
||||
context->end.set(newCommandBuffer.second);
|
||||
|
||||
// Wait for rsx to "release" the new command buffer
|
||||
while (!Emu.IsStopped())
|
||||
while (true)
|
||||
{
|
||||
u32 getPos = ctrl.get.load();
|
||||
if (isInCommandBufferExcept(getPos, newCommandBuffer.first, newCommandBuffer.second))
|
||||
break;
|
||||
std::chrono::time_point<std::chrono::system_clock> waitPoint = std::chrono::system_clock::now();
|
||||
long long elapsedTime = std::chrono::duration_cast<std::chrono::seconds>(waitPoint - enterWait).count();
|
||||
if (elapsedTime > 0)
|
||||
cellGcmSys.error("Has wait for more than a second for command buffer to be released by RSX");
|
||||
std::this_thread::yield();
|
||||
|
||||
ppu.test_state();
|
||||
busy_wait();
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
|
||||
@@ -61,7 +61,7 @@ s32 cellGifDecOpen(PMainHandle mainHandle, PPSubHandle subHandle, PSrc src, POpe
|
||||
if (!file_s) return CELL_GIFDEC_ERROR_OPEN_FILE;
|
||||
|
||||
current_subHandle.fileSize = file_s.size();
|
||||
current_subHandle.fd = idm::make<lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0);
|
||||
current_subHandle.fd = idm::make<lv2_fs_object, lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,7 @@ s32 cellGifDecReadHeader(PMainHandle mainHandle, PSubHandle subHandle, PInfo inf
|
||||
|
||||
case CELL_GIFDEC_FILE:
|
||||
{
|
||||
auto file = idm::get<lv2_file>(fd);
|
||||
auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
file->file.seek(0);
|
||||
file->file.read(buffer, sizeof(buffer));
|
||||
break;
|
||||
@@ -181,7 +181,7 @@ s32 cellGifDecDecodeData(PMainHandle mainHandle, PSubHandle subHandle, vm::ptr<u
|
||||
|
||||
case CELL_GIFDEC_FILE:
|
||||
{
|
||||
auto file = idm::get<lv2_file>(fd);
|
||||
auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
file->file.seek(0);
|
||||
file->file.read(gif.get(), fileSize);
|
||||
break;
|
||||
@@ -283,7 +283,7 @@ s32 cellGifDecClose(PMainHandle mainHandle, PSubHandle subHandle)
|
||||
{
|
||||
cellGifDec.warning("cellGifDecClose(mainHandle=*0x%x, subHandle=*0x%x)", mainHandle, subHandle);
|
||||
|
||||
idm::remove<lv2_file>(subHandle->fd);
|
||||
idm::remove<lv2_fs_object, lv2_file>(subHandle->fd);
|
||||
|
||||
vm::dealloc(subHandle.addr());
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ s32 cellJpgDecOpen(u32 mainHandle, vm::ptr<u32> subHandle, vm::ptr<CellJpgDecSrc
|
||||
if (!file_s) return CELL_JPGDEC_ERROR_OPEN_FILE;
|
||||
|
||||
current_subHandle.fileSize = file_s.size();
|
||||
current_subHandle.fd = idm::make<lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0);
|
||||
current_subHandle.fd = idm::make<lv2_fs_object, lv2_file>(src->fileName.get_ptr(), std::move(file_s), 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ s32 cellJpgDecClose(u32 mainHandle, u32 subHandle)
|
||||
return CELL_JPGDEC_ERROR_FATAL;
|
||||
}
|
||||
|
||||
idm::remove<lv2_file>(subHandle_data->fd);
|
||||
idm::remove<lv2_fs_object, lv2_file>(subHandle_data->fd);
|
||||
idm::remove<CellJpgDecSubHandle>(subHandle);
|
||||
|
||||
return CELL_OK;
|
||||
@@ -110,7 +110,7 @@ s32 cellJpgDecReadHeader(u32 mainHandle, u32 subHandle, vm::ptr<CellJpgDecInfo>
|
||||
|
||||
case CELL_JPGDEC_FILE:
|
||||
{
|
||||
auto file = idm::get<lv2_file>(fd);
|
||||
auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
file->file.seek(0);
|
||||
file->file.read(buffer.get(), fileSize);
|
||||
break;
|
||||
@@ -189,7 +189,7 @@ s32 cellJpgDecDecodeData(u32 mainHandle, u32 subHandle, vm::ptr<u8> data, vm::cp
|
||||
|
||||
case CELL_JPGDEC_FILE:
|
||||
{
|
||||
auto file = idm::get<lv2_file>(fd);
|
||||
auto file = idm::get<lv2_fs_object, lv2_file>(fd);
|
||||
file->file.seek(0);
|
||||
file->file.read(jpg.get(), fileSize);
|
||||
break;
|
||||
|
||||
@@ -109,6 +109,10 @@ struct CellJpgDecDataOutInfo
|
||||
// Custom structs
|
||||
struct CellJpgDecSubHandle
|
||||
{
|
||||
static const u32 id_base = 1;
|
||||
static const u32 id_step = 1;
|
||||
static const u32 id_count = 1023;
|
||||
|
||||
u32 fd;
|
||||
u64 fileSize;
|
||||
CellJpgDecInfo info;
|
||||
|
||||
+503
-301
File diff suppressed because it is too large
Load Diff
@@ -52,20 +52,26 @@ enum
|
||||
L10N_CODEPAGE_863,
|
||||
L10N_CODEPAGE_866,
|
||||
L10N_CODEPAGE_932,
|
||||
L10N_SHIFT_JIS,
|
||||
L10N_CODEPAGE_936,
|
||||
L10N_GBK,
|
||||
L10N_CODEPAGE_949,
|
||||
L10N_UHC,
|
||||
L10N_CODEPAGE_950,
|
||||
L10N_BIG5,
|
||||
L10N_CODEPAGE_1251,
|
||||
L10N_CODEPAGE_1252,
|
||||
L10N_EUC_CN,
|
||||
L10N_EUC_JP,
|
||||
L10N_EUC_KR,
|
||||
L10N_ISO_2022_JP,
|
||||
L10N_JIS,
|
||||
L10N_ARIB,
|
||||
L10N_HZ,
|
||||
L10N_GB18030,
|
||||
L10N_RIS_506,
|
||||
//FW 3.10 and above
|
||||
L10N_MUSIC_SHIFT_JIS,
|
||||
//FW 3.10 and below
|
||||
L10N_CODEPAGE_852,
|
||||
L10N_CODEPAGE_1250,
|
||||
L10N_CODEPAGE_737,
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
|
||||
extern logs::channel cellSysutil;
|
||||
|
||||
s32 cellMsgDialogOpen()
|
||||
MsgDialogBase::~MsgDialogBase()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
}
|
||||
|
||||
s32 cellMsgDialogOpen2(u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialogCallback> callback, vm::ptr<void> userData, vm::ptr<void> extParam)
|
||||
@@ -114,13 +113,19 @@ s32 cellMsgDialogOpen2(u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialog
|
||||
|
||||
while (!result)
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
std::this_thread::sleep_for(1ms);
|
||||
thread_ctrl::wait_for(1000);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMsgDialogOpen(u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialogCallback> callback, vm::ptr<void> userData, vm::ptr<void> extParam)
|
||||
{
|
||||
//Note: This function needs proper implementation, solve first argument "type" conflict with MsgDialogOpen2 in cellMsgDialog.h.
|
||||
cellSysutil.todo("cellMsgDialogOpen(type=0x%x, msgString=%s, callback=*0x%x, userData=*0x%x, extParam=*0x%x)", type, msgString, callback, userData, extParam);
|
||||
return cellMsgDialogOpen2(type, msgString, callback, userData, extParam);
|
||||
}
|
||||
|
||||
s32 cellMsgDialogOpenErrorCode(ppu_thread& ppu, u32 errorCode, vm::ptr<CellMsgDialogCallback> callback, vm::ptr<void> userData, vm::ptr<void> extParam)
|
||||
{
|
||||
cellSysutil.warning("cellMsgDialogOpenErrorCode(errorCode=0x%x, callback=*0x%x, userData=*0x%x, extParam=*0x%x)", errorCode, callback, userData, extParam);
|
||||
@@ -264,7 +269,7 @@ s32 cellMsgDialogProgressBarSetMsg(u32 progressBarIndex, vm::cptr<char> msgStrin
|
||||
return CELL_MSGDIALOG_ERROR_DIALOG_NOT_OPENED;
|
||||
}
|
||||
|
||||
if (progressBarIndex >= dlg->type.progress_bar_count)
|
||||
if (progressBarIndex >= dlg->type.progress_bar_count || !msgString)
|
||||
{
|
||||
return CELL_MSGDIALOG_ERROR_PARAM;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
|
||||
std::function<void(s32 status)> on_close;
|
||||
|
||||
virtual ~MsgDialogBase() = default;
|
||||
virtual ~MsgDialogBase();
|
||||
virtual void Create(const std::string& msg) = 0;
|
||||
virtual void CreateOsk(const std::string& msg, char16_t* osk_text) = 0;
|
||||
virtual void ProgressBarSetMsg(u32 progressBarIndex, const std::string& msg) = 0;
|
||||
|
||||
@@ -16,97 +16,116 @@ struct music2_t
|
||||
|
||||
s32 cellMusicGetSelectionContext()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicSetSelectionContext2()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicSetVolume2()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicGetContentsId()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicSetSelectionContext()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicInitialize2SystemWorkload()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicGetPlaybackStatus2()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicGetContentsId2()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicFinalize()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicInitializeSystemWorkload()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicInitialize()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicFinalize2()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicGetSelectionContext2()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicGetVolume()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicGetPlaybackStatus()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicSetPlaybackCommand2()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicSetPlaybackCommand()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicSelectContents2()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicSelectContents()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicInitialize2(s32 mode, s32 spuPriority, vm::ptr<CellMusic2Callback> func, vm::ptr<void> userData)
|
||||
@@ -134,12 +153,14 @@ s32 cellMusicInitialize2(s32 mode, s32 spuPriority, vm::ptr<CellMusic2Callback>
|
||||
|
||||
s32 cellMusicSetVolume()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellMusicGetVolume2()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellMusic);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -37,8 +37,7 @@ s32 cellOskDialogLoadAsync(u32 container, vm::ptr<CellOskDialogParam> dialogPara
|
||||
|
||||
while (!result)
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
std::this_thread::sleep_for(1ms);
|
||||
thread_ctrl::wait_for(1000);
|
||||
}
|
||||
|
||||
return CELL_OSKDIALOG_OK;
|
||||
@@ -131,14 +130,15 @@ s32 cellOskDialogExtInputDeviceUnlock()
|
||||
return CELL_OSKDIALOG_OK;
|
||||
}
|
||||
|
||||
s32 cellOskDialogExtRegisterKeyboardEventHookCallback()
|
||||
s32 cellOskDialogExtRegisterKeyboardEventHookCallback(u16 hookEventMode, vm::ptr<cellOskDialogHardwareKeyboardEventHookCallback> pCallback)
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
cellOskDialog.todo("cellOskDialogExtRegisterKeyboardEventHookCallback(hookEventMode=%u, pCallback=*0x%x)", hookEventMode, pCallback);
|
||||
return CELL_OSKDIALOG_OK;
|
||||
}
|
||||
|
||||
s32 cellOskDialogExtAddJapaneseOptionDictionary(vm::cptr<char> filePath)
|
||||
s32 cellOskDialogExtAddJapaneseOptionDictionary(vm::cpptr<char> filePath)
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtAddJapaneseOptionDictionary(filePath=*0x%0x)", filePath);
|
||||
cellOskDialog.todo("cellOskDialogExtAddJapaneseOptionDictionary(filePath=**0x%0x)", filePath);
|
||||
return CELL_OSKDIALOG_OK;
|
||||
}
|
||||
|
||||
@@ -148,9 +148,9 @@ s32 cellOskDialogExtEnableClipboard()
|
||||
return CELL_OSKDIALOG_OK;
|
||||
}
|
||||
|
||||
s32 cellOskDialogExtSendFinishMessage(vm::ptr<CellOskDialogFinishReason> finishReason)
|
||||
s32 cellOskDialogExtSendFinishMessage(s32 /*CellOskDialogFinishReason*/ finishReason)
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtSendFinishMessage(finishReason=*0x%x)", finishReason);
|
||||
cellOskDialog.todo("cellOskDialogExtSendFinishMessage(finishReason=%d)", finishReason);
|
||||
return CELL_OSKDIALOG_OK;
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ s32 cellOskDialogExtAddOptionDictionary(vm::cptr<CellOskDialogImeDictionaryInfo>
|
||||
|
||||
s32 cellOskDialogExtSetInitialScale(f32 initialScale)
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtSetInitialScale(initialScale=0x%x)", initialScale);
|
||||
cellOskDialog.todo("cellOskDialogExtSetInitialScale(initialScale=%f)", initialScale);
|
||||
return CELL_OSKDIALOG_OK;
|
||||
}
|
||||
|
||||
@@ -174,24 +174,25 @@ s32 cellOskDialogExtInputDeviceLock()
|
||||
|
||||
s32 cellOskDialogExtSetBaseColor(f32 red, f32 blue, f32 green, f32 alpha)
|
||||
{
|
||||
cellOskDialog.warning("cellOskDialogExtSetBaseColor(red=0x%x, blue=0x%x, green=0x%x, alpha=0x%x)", red, blue, green, alpha);
|
||||
cellOskDialog.warning("cellOskDialogExtSetBaseColor(red=%f, blue=%f, green=%f, alpha=%f)", red, blue, green, alpha);
|
||||
return CELL_OSKDIALOG_OK;
|
||||
}
|
||||
|
||||
s32 cellOskDialogExtRegisterConfirmWordFilterCallback()
|
||||
s32 cellOskDialogExtRegisterConfirmWordFilterCallback(vm::ptr<cellOskDialogConfirmWordFilterCallback> pCallback)
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
cellOskDialog.todo("cellOskDialogExtRegisterConfirmWordFilterCallback(pCallback=*0x%x)", pCallback);
|
||||
return CELL_OSKDIALOG_OK;
|
||||
}
|
||||
|
||||
s32 cellOskDialogExtUpdateInputText()
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtUpdateInputText");
|
||||
cellOskDialog.todo("cellOskDialogExtUpdateInputText()");
|
||||
return CELL_OSKDIALOG_OK;
|
||||
}
|
||||
|
||||
s32 cellOskDialogExtDisableHalfByteKana()
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtDisableHalfByteKana");
|
||||
cellOskDialog.todo("cellOskDialogExtDisableHalfByteKana()");
|
||||
return CELL_OSKDIALOG_OK;
|
||||
}
|
||||
|
||||
@@ -203,7 +204,7 @@ s32 cellOskDialogExtSetPointerEnable(b8 enable)
|
||||
|
||||
s32 cellOskDialogExtUpdatePointerDisplayPos()
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtUpdatePointerDisplayPos()");
|
||||
cellOskDialog.todo("cellOskDialogExtUpdatePointerDisplayPos"); // Missing arguments
|
||||
return CELL_OSKDIALOG_OK;
|
||||
}
|
||||
|
||||
@@ -213,9 +214,9 @@ s32 cellOskDialogExtEnableHalfByteKana()
|
||||
return CELL_OSKDIALOG_OK;
|
||||
}
|
||||
|
||||
s32 cellOskDialogExtRegisterForceFinishCallback()
|
||||
s32 cellOskDialogExtRegisterForceFinishCallback(vm::ptr<cellOskDialogForceFinishCallback> pCallback)
|
||||
{
|
||||
cellOskDialog.todo("cellOskDialogExtRegisterForceFinishCallback()");
|
||||
cellOskDialog.todo("cellOskDialogExtRegisterForceFinishCallback(pCallback=*0x%x)", pCallback);
|
||||
return CELL_OSKDIALOG_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -157,3 +157,7 @@ struct CellOskDialogImeDictionaryInfo
|
||||
vm::bcptr<char> dictionaryPath;
|
||||
};
|
||||
|
||||
using cellOskDialogConfirmWordFilterCallback = int(vm::ptr<u16> pConfirmString, s32 wordLength);
|
||||
using cellOskDialogHardwareKeyboardEventHookCallback = class b8(
|
||||
vm::ptr<CellOskDialogKeyMessage> keyMessage, vm::ptr<u32> action, vm::ptr<void> pActionInfo);
|
||||
using cellOskDialogForceFinishCallback = class b8();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
namespace vm { using namespace ps3; }
|
||||
|
||||
logs::channel cellOvis("cellOvis", logs::level::notice);
|
||||
|
||||
// Return Codes
|
||||
|
||||
@@ -12,15 +12,12 @@ s32 cellPadInit(u32 max_connect)
|
||||
{
|
||||
sys_io.warning("cellPadInit(max_connect=%d)", max_connect);
|
||||
|
||||
if (max_connect > CELL_PAD_MAX_PORT_NUM)
|
||||
return CELL_PAD_ERROR_INVALID_PARAMETER;
|
||||
|
||||
const auto handler = fxm::import<PadHandlerBase>(Emu.GetCallbacks().get_pad_handler);
|
||||
|
||||
if (!handler)
|
||||
return CELL_PAD_ERROR_ALREADY_INITIALIZED;
|
||||
|
||||
handler->Init(max_connect);
|
||||
handler->Init(std::min(max_connect, CELL_PAD_MAX_PORT_NUM));
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ void pngDecReadBuffer(png_structp png_ptr, png_bytep out, png_size_t length)
|
||||
if (buffer.file)
|
||||
{
|
||||
// Get the file
|
||||
auto file = idm::get<lv2_file>(buffer.fd);
|
||||
auto file = idm::get<lv2_fs_object, lv2_file>(buffer.fd);
|
||||
|
||||
// Read the data
|
||||
file->file.read(out, length);
|
||||
@@ -83,10 +83,75 @@ void pngDecReadBuffer(png_structp png_ptr, png_bytep out, png_size_t length)
|
||||
}
|
||||
}
|
||||
|
||||
void pngDecRowCallback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass)
|
||||
{
|
||||
PngStream* stream = (PngStream*)png_get_progressive_ptr(png_ptr);
|
||||
if (!stream)
|
||||
{
|
||||
cellPngDec.error("Failed to obtain streamPtr in rowCallback.");
|
||||
return;
|
||||
}
|
||||
|
||||
// we have to check this everytime as this func can be called multiple times per row, and/or only once per row
|
||||
if (stream->nextRow + stream->outputCounts == row_num)
|
||||
stream->nextRow = row_num;
|
||||
|
||||
if (stream->ppuContext && (stream->nextRow == row_num || pass > 0))
|
||||
{
|
||||
if (pass > 0 )
|
||||
{
|
||||
stream->cbDispInfo->scanPassCount = pass;
|
||||
stream->cbDispInfo->nextOutputStartY = row_num;
|
||||
}
|
||||
else {
|
||||
stream->cbDispInfo->scanPassCount = 0;
|
||||
stream->cbDispInfo->nextOutputStartY = 0;
|
||||
}
|
||||
|
||||
stream->cbDispInfo->outputImage = stream->cbDispParam->nextOutputImage;
|
||||
stream->cbCtrlDisp.cbCtrlDispFunc(*stream->ppuContext, stream->cbDispInfo, stream->cbDispParam, stream->cbCtrlDisp.cbCtrlDispArg);
|
||||
stream->cbDispInfo->outputStartY = row_num;
|
||||
}
|
||||
u8* data;
|
||||
if (pass > 0)
|
||||
data = static_cast<u8*>(stream->cbDispParam->nextOutputImage.get_ptr());
|
||||
else
|
||||
data = static_cast<u8*>(stream->cbDispParam->nextOutputImage.get_ptr()) + ((row_num - stream->cbDispInfo->outputStartY) * stream->cbDispInfo->outputFrameWidthByte);
|
||||
|
||||
png_progressive_combine_row(png_ptr, data, new_row);
|
||||
}
|
||||
|
||||
void pngDecInfoCallback(png_structp png_ptr, png_infop info)
|
||||
{
|
||||
PngStream* stream = (PngStream*)png_get_progressive_ptr(png_ptr);
|
||||
if (!stream)
|
||||
{
|
||||
cellPngDec.error("Failed to obtain streamPtr in rowCallback.");
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t remaining = png_process_data_pause(png_ptr, false);
|
||||
stream->buffer->cursor += (stream->buffer->length - remaining);
|
||||
}
|
||||
|
||||
void pngDecEndCallback(png_structp png_ptr, png_infop info)
|
||||
{
|
||||
PngStream* stream = (PngStream*)png_get_progressive_ptr(png_ptr);
|
||||
if (!stream)
|
||||
{
|
||||
cellPngDec.error("Failed to obtain streamPtr in endCallback.");
|
||||
return;
|
||||
}
|
||||
|
||||
stream->endOfFile = true;
|
||||
}
|
||||
|
||||
// Custom error handler for libpng
|
||||
void pngDecError(png_structp png_ptr, png_const_charp error_message)
|
||||
{
|
||||
cellPngDec.error(error_message);
|
||||
// we can't return here or libpng blows up
|
||||
throw LibPngCustomException("Fatal Error in libpng");
|
||||
}
|
||||
|
||||
// Custom warning handler for libpng
|
||||
@@ -114,7 +179,7 @@ void pngDecWarning(png_structp png_ptr, png_const_charp error_message)
|
||||
// 14 - sCAL
|
||||
// 15 - IDAT
|
||||
// 16:30 - reserved
|
||||
be_t<u32> pngDecGetChunkInformation(PStream stream, bool IDAT = false)
|
||||
be_t<u32> pngDecGetChunkInformation(PngStream* stream, bool IDAT = false)
|
||||
{
|
||||
// The end result of the chunk information (bigger-endian)
|
||||
be_t<u32> chunk_information = 0;
|
||||
@@ -263,9 +328,9 @@ s32 pngDecCreate(ppu_thread& ppu, PPHandle png_handle, PThreadInParam thread_in_
|
||||
}
|
||||
|
||||
// Set the allocation functions in the handle
|
||||
handle->malloc = thread_in_param->cbCtrlMallocFunc;
|
||||
handle->malloc_ = thread_in_param->cbCtrlMallocFunc;
|
||||
handle->malloc_arg = thread_in_param->cbCtrlMallocArg;
|
||||
handle->free = thread_in_param->cbCtrlFreeFunc;
|
||||
handle->free_ = thread_in_param->cbCtrlFreeFunc;
|
||||
handle->free_arg = thread_in_param->cbCtrlFreeArg;
|
||||
|
||||
// Set handle pointer
|
||||
@@ -280,7 +345,7 @@ s32 pngDecCreate(ppu_thread& ppu, PPHandle png_handle, PThreadInParam thread_in_
|
||||
s32 pngDecDestroy(ppu_thread& ppu, PHandle handle)
|
||||
{
|
||||
// Deallocate the decoder handle memory
|
||||
if (handle->free(ppu, handle, handle->free_arg) != 0)
|
||||
if (handle->free_(ppu, handle, handle->free_arg) != 0)
|
||||
{
|
||||
cellPngDec.error("PNG decoder deallocation failed.");
|
||||
return CELL_PNGDEC_ERROR_FATAL;
|
||||
@@ -291,14 +356,15 @@ s32 pngDecDestroy(ppu_thread& ppu, PHandle handle)
|
||||
|
||||
s32 pngDecOpen(ppu_thread& ppu, PHandle handle, PPStream png_stream, PSrc source, POpenInfo open_info, PCbControlStream control_stream = vm::null, POpenParam open_param = vm::null)
|
||||
{
|
||||
// Check if partial image decoding is used
|
||||
if (control_stream || open_param)
|
||||
// partial decoding only supported with buffer type
|
||||
if (source->srcSelect != CELL_PNGDEC_BUFFER && control_stream)
|
||||
{
|
||||
fmt::throw_exception("Partial image decoding is not supported." HERE);
|
||||
cellPngDec.error("Attempted partial image decode with file.");
|
||||
return CELL_PNGDEC_ERROR_STREAM_FORMAT;
|
||||
}
|
||||
|
||||
// Allocate memory for the stream structure
|
||||
auto stream = vm::ptr<PngStream>::make(handle->malloc(ppu, sizeof(PngStream), handle->malloc_arg).addr());
|
||||
auto stream = vm::ptr<PngStream>::make(handle->malloc_(ppu, sizeof(PngStream), handle->malloc_arg).addr());
|
||||
|
||||
// Check if the allocation of memory for the stream structure failed
|
||||
if (!stream)
|
||||
@@ -313,14 +379,11 @@ s32 pngDecOpen(ppu_thread& ppu, PHandle handle, PPStream png_stream, PSrc source
|
||||
// Set the stream source to the source give by the game
|
||||
stream->source = *source;
|
||||
|
||||
// Indicate that a fixed alpha value isn't used, if not specified otherwise
|
||||
stream->fixed_alpha = false;
|
||||
|
||||
// Use virtual memory address as a handle
|
||||
*png_stream = stream;
|
||||
|
||||
// Allocate memory for the PNG buffer for decoding
|
||||
auto buffer = vm::ptr<PngBuffer>::make(handle->malloc(ppu, sizeof(PngBuffer), handle->malloc_arg).addr());
|
||||
auto buffer = vm::ptr<PngBuffer>::make(handle->malloc_(ppu, sizeof(PngBuffer), handle->malloc_arg).addr());
|
||||
|
||||
// Check for if the buffer structure allocation failed
|
||||
if (!buffer)
|
||||
@@ -364,7 +427,7 @@ s32 pngDecOpen(ppu_thread& ppu, PHandle handle, PPStream png_stream, PSrc source
|
||||
}
|
||||
|
||||
// Get the file descriptor
|
||||
buffer->fd = idm::make<lv2_file>(stream->source.fileName.get_ptr(), std::move(file_stream), 0, 0);
|
||||
buffer->fd = idm::make<lv2_fs_object, lv2_file>(stream->source.fileName.get_ptr(), std::move(file_stream), 0, 0);
|
||||
|
||||
// Indicate that we need to read from a file stream
|
||||
buffer->file = true;
|
||||
@@ -401,43 +464,44 @@ s32 pngDecOpen(ppu_thread& ppu, PHandle handle, PPStream png_stream, PSrc source
|
||||
fmt::throw_exception("Creation of png_infop failed." HERE);
|
||||
}
|
||||
|
||||
// Set a point to return to when an error occurs in libpng
|
||||
if (setjmp(png_jmpbuf(stream->png_ptr)))
|
||||
{
|
||||
fmt::throw_exception("Fatal error in libpng." HERE);
|
||||
}
|
||||
|
||||
// We must indicate, that we allocated more memory
|
||||
open_info->initSpaceAllocated += sizeof(PngBuffer);
|
||||
|
||||
// Init the IO for either reading from a file or a buffer
|
||||
if (source->srcSelect == CELL_PNGDEC_BUFFER)
|
||||
{
|
||||
// Set the data pointer and the file size
|
||||
buffer->length = stream->source.fileSize;
|
||||
buffer->length = stream->source.streamSize;
|
||||
buffer->data = stream->source.streamPtr;
|
||||
|
||||
// Since we already read the header, we start reading from position 8
|
||||
buffer->cursor = 8;
|
||||
}
|
||||
|
||||
// Set the custom read function for decoding
|
||||
png_set_read_fn(stream->png_ptr, buffer.get_ptr(), pngDecReadBuffer);
|
||||
if (control_stream)
|
||||
{
|
||||
if (open_param && open_param->selectChunk != 0)
|
||||
fmt::throw_exception("Partial Decoding with selectChunk not supported yet.");
|
||||
|
||||
// We need to tell libpng, that we already read 8 bytes
|
||||
png_set_sig_bytes(stream->png_ptr, 8);
|
||||
stream->cbCtrlStream.cbCtrlStrmArg = control_stream->cbCtrlStrmArg;
|
||||
stream->cbCtrlStream.cbCtrlStrmFunc = control_stream->cbCtrlStrmFunc;
|
||||
|
||||
// Read the basic info of the PNG file
|
||||
png_read_info(stream->png_ptr, stream->info_ptr);
|
||||
png_set_progressive_read_fn(stream->png_ptr, (void *)stream.get_ptr(), pngDecInfoCallback, pngDecRowCallback, pngDecEndCallback);
|
||||
|
||||
// Read the header info for future use
|
||||
stream->info.imageWidth = png_get_image_width(stream->png_ptr, stream->info_ptr);
|
||||
stream->info.imageHeight = png_get_image_height(stream->png_ptr, stream->info_ptr);
|
||||
stream->info.numComponents = png_get_channels(stream->png_ptr, stream->info_ptr);
|
||||
stream->info.colorSpace = getPngDecColourType(png_get_color_type(stream->png_ptr, stream->info_ptr));
|
||||
stream->info.bitDepth = png_get_bit_depth(stream->png_ptr, stream->info_ptr);
|
||||
stream->info.interlaceMethod = png_get_interlace_type(stream->png_ptr, stream->info_ptr);
|
||||
stream->info.chunkInformation = pngDecGetChunkInformation(stream);
|
||||
// push header tag to libpng to keep us in sync
|
||||
try
|
||||
{
|
||||
png_process_data(stream->png_ptr, stream->info_ptr, header, 8);
|
||||
}
|
||||
catch (LibPngCustomException&)
|
||||
{
|
||||
return CELL_PNGDEC_ERROR_HEADER;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
png_set_read_fn(stream->png_ptr, buffer.get_ptr(), pngDecReadBuffer);
|
||||
|
||||
// We need to tell libpng, that we already read 8 bytes
|
||||
png_set_sig_bytes(stream->png_ptr, 8);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -447,13 +511,13 @@ s32 pngDecClose(ppu_thread& ppu, PHandle handle, PStream stream)
|
||||
// Remove the file descriptor, if a file descriptor was used for decoding
|
||||
if (stream->buffer->file)
|
||||
{
|
||||
idm::remove<lv2_file>(stream->buffer->fd);
|
||||
idm::remove<lv2_fs_object, lv2_file>(stream->buffer->fd);
|
||||
}
|
||||
|
||||
// Deallocate the PNG buffer structure used to decode from memory, if we decoded from memory
|
||||
if (stream->buffer)
|
||||
{
|
||||
if (handle->free(ppu, stream->buffer, handle->free_arg) != 0)
|
||||
if (handle->free_(ppu, stream->buffer, handle->free_arg) != 0)
|
||||
{
|
||||
cellPngDec.error("PNG buffer decoding structure deallocation failed.");
|
||||
return CELL_PNGDEC_ERROR_FATAL;
|
||||
@@ -464,7 +528,7 @@ s32 pngDecClose(ppu_thread& ppu, PHandle handle, PStream stream)
|
||||
png_destroy_read_struct(&stream->png_ptr, &stream->info_ptr, nullptr);
|
||||
|
||||
// Deallocate the stream memory
|
||||
if (handle->free(ppu, stream, handle->free_arg) != 0)
|
||||
if (handle->free_(ppu, stream, handle->free_arg) != 0)
|
||||
{
|
||||
cellPngDec.error("PNG stream deallocation failed.");
|
||||
return CELL_PNGDEC_ERROR_FATAL;
|
||||
@@ -473,83 +537,139 @@ s32 pngDecClose(ppu_thread& ppu, PHandle handle, PStream stream)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 pngReadHeader(PStream stream, PInfo info, PExtInfo extra_info = vm::null)
|
||||
void pngSetHeader(PngStream* stream)
|
||||
{
|
||||
// Set the pointer to stream info - we already get the header info, when opening the decoder
|
||||
*info = stream->info;
|
||||
|
||||
// Set the reserved value to 0, if passed to the function.
|
||||
if (extra_info)
|
||||
{
|
||||
extra_info->reserved = 0;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
stream->info.imageWidth = png_get_image_width(stream->png_ptr, stream->info_ptr);
|
||||
stream->info.imageHeight = png_get_image_height(stream->png_ptr, stream->info_ptr);
|
||||
stream->info.numComponents = png_get_channels(stream->png_ptr, stream->info_ptr);
|
||||
stream->info.colorSpace = getPngDecColourType(png_get_color_type(stream->png_ptr, stream->info_ptr));
|
||||
stream->info.bitDepth = png_get_bit_depth(stream->png_ptr, stream->info_ptr);
|
||||
stream->info.interlaceMethod = png_get_interlace_type(stream->png_ptr, stream->info_ptr);
|
||||
stream->info.chunkInformation = pngDecGetChunkInformation(stream);
|
||||
}
|
||||
|
||||
s32 pngDecSetParameter(PStream stream, PInParam in_param, POutParam out_param, PExtInParam extra_in_param = vm::null, PExtOutParam extra_out_param = vm::null)
|
||||
{
|
||||
// Partial image decoding is not supported. Need to find games to test with.
|
||||
if (extra_in_param || extra_out_param)
|
||||
{
|
||||
fmt::throw_exception("Partial image decoding is not supported" HERE);
|
||||
}
|
||||
|
||||
if (in_param->outputPackFlag == CELL_PNGDEC_1BYTE_PER_NPIXEL)
|
||||
{
|
||||
fmt::throw_exception("Packing not supported! (%d)" HERE, in_param->outputPackFlag);
|
||||
}
|
||||
|
||||
// We already grab the basic info, when opening the stream, so we simply need to pass most of the values
|
||||
// flag to keep unknown chunks
|
||||
png_set_keep_unknown_chunks(stream->png_ptr, PNG_HANDLE_CHUNK_IF_SAFE, 0, 0);
|
||||
|
||||
// Scale 16 bit depth down to 8 bit depth.
|
||||
if (stream->info.bitDepth == 16 && in_param->outputBitDepth == 8)
|
||||
{
|
||||
// PS3 uses png_set_strip_16, since png_set_scale_16 wasn't available back then.
|
||||
png_set_strip_16(stream->png_ptr);
|
||||
}
|
||||
|
||||
// This shouldnt ever happen, but not sure what to do if it does, just want it logged for now
|
||||
if (stream->info.bitDepth != 16 && in_param->outputBitDepth == 16)
|
||||
cellPngDec.error("Output depth of 16 with non input depth of 16 specified!");
|
||||
if (in_param->commandPtr != vm::null)
|
||||
cellPngDec.warning("Ignoring CommandPtr.");
|
||||
|
||||
if (stream->info.colorSpace != in_param->outputColorSpace)
|
||||
{
|
||||
// check if we need to set alpha
|
||||
const bool inputHasAlpha = cellPngColorSpaceHasAlpha(stream->info.colorSpace);
|
||||
const bool outputWantsAlpha = cellPngColorSpaceHasAlpha(in_param->outputColorSpace);
|
||||
|
||||
if (outputWantsAlpha && !inputHasAlpha)
|
||||
{
|
||||
if (in_param->outputAlphaSelect == CELL_PNGDEC_FIX_ALPHA)
|
||||
png_set_add_alpha(stream->png_ptr, in_param->outputColorAlpha, in_param->outputColorSpace == CELL_PNGDEC_ARGB ? PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
|
||||
else
|
||||
{
|
||||
// Check if we can steal the alpha from a trns block
|
||||
if (png_get_valid(stream->png_ptr, stream->info_ptr, PNG_INFO_tRNS))
|
||||
png_set_tRNS_to_alpha(stream->png_ptr);
|
||||
// if not, just set default of 0xff
|
||||
else
|
||||
png_set_add_alpha(stream->png_ptr, 0xff, in_param->outputColorSpace == CELL_PNGDEC_ARGB ? PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
|
||||
}
|
||||
}
|
||||
else if (inputHasAlpha && !outputWantsAlpha)
|
||||
png_set_strip_alpha(stream->png_ptr);
|
||||
else if (in_param->outputColorSpace == CELL_PNGDEC_ARGB && stream->info.colorSpace == CELL_PNGDEC_RGBA)
|
||||
png_set_swap_alpha(stream->png_ptr);
|
||||
|
||||
// Handle gray<->rgb colorspace conversions
|
||||
// rgb output
|
||||
if (in_param->outputColorSpace == CELL_PNGDEC_ARGB
|
||||
|| in_param->outputColorSpace == CELL_PNGDEC_RGBA
|
||||
|| in_param->outputColorSpace == CELL_PNGDEC_RGB)
|
||||
{
|
||||
|
||||
if (stream->info.colorSpace == CELL_PNGDEC_PALETTE)
|
||||
png_set_palette_to_rgb(stream->png_ptr);
|
||||
if ((stream->info.colorSpace == CELL_PNGDEC_GRAYSCALE || stream->info.colorSpace == CELL_PNGDEC_GRAYSCALE_ALPHA)
|
||||
&& stream->info.bitDepth < 8)
|
||||
png_set_expand_gray_1_2_4_to_8(stream->png_ptr);
|
||||
}
|
||||
// grayscale output
|
||||
else
|
||||
{
|
||||
if (stream->info.colorSpace == CELL_PNGDEC_ARGB
|
||||
|| stream->info.colorSpace == CELL_PNGDEC_RGBA
|
||||
|| stream->info.colorSpace == CELL_PNGDEC_RGB)
|
||||
{
|
||||
|
||||
png_set_rgb_to_gray(stream->png_ptr, PNG_ERROR_ACTION_NONE, PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT);
|
||||
}
|
||||
else {
|
||||
// not sure what to do here
|
||||
cellPngDec.error("Grayscale / Palette to Grayscale / Palette conversion currently unsupported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stream->passes = png_set_interlace_handling(stream->png_ptr);
|
||||
|
||||
// Update the info structure
|
||||
png_read_update_info(stream->png_ptr, stream->info_ptr);
|
||||
|
||||
stream->out_param.outputWidth = stream->info.imageWidth;
|
||||
stream->out_param.outputHeight = stream->info.imageHeight;
|
||||
stream->out_param.outputBitDepth = in_param->outputBitDepth;
|
||||
stream->out_param.outputColorSpace = in_param->outputColorSpace;
|
||||
stream->out_param.outputBitDepth = stream->info.bitDepth;
|
||||
stream->out_param.outputMode = in_param->outputMode;
|
||||
|
||||
stream->out_param.outputWidthByte = png_get_rowbytes(stream->png_ptr, stream->info_ptr);
|
||||
stream->out_param.outputComponents = png_get_channels(stream->png_ptr, stream->info_ptr);
|
||||
|
||||
stream->packing = in_param->outputPackFlag;
|
||||
|
||||
// Check if a fixed alpha value is specified
|
||||
if (in_param->outputAlphaSelect == CELL_PNGDEC_FIX_ALPHA)
|
||||
{
|
||||
// We set the fixed alpha value in the stream structure, for padding while decoding
|
||||
stream->fixed_alpha = true;
|
||||
stream->fixed_alpha_colour = in_param->outputColorAlpha;
|
||||
}
|
||||
|
||||
// Remap the number of output components based on the passed colorSpace value
|
||||
switch (stream->out_param.outputColorSpace)
|
||||
{
|
||||
case CELL_PNGDEC_RGBA:
|
||||
case CELL_PNGDEC_ARGB:
|
||||
{
|
||||
stream->out_param.outputComponents = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_PNGDEC_RGB:
|
||||
{
|
||||
stream->out_param.outputComponents = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_PNGDEC_GRAYSCALE_ALPHA:
|
||||
{
|
||||
stream->out_param.outputComponents = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_PNGDEC_PALETTE:
|
||||
case CELL_PNGDEC_GRAYSCALE:
|
||||
{
|
||||
stream->out_param.outputComponents = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the memory usage. We currently don't actually allocate memory for libpng through the callbacks, due to libpng needing a lot more memory compared to PS3 variant.
|
||||
stream->out_param.useMemorySpace = 0;
|
||||
|
||||
// Set the pointer
|
||||
if (extra_in_param)
|
||||
{
|
||||
if (extra_in_param->bufferMode != CELL_PNGDEC_LINE_MODE)
|
||||
{
|
||||
cellPngDec.error("Invalid Buffermode specified.");
|
||||
return CELL_PNGDEC_ERROR_ARG;
|
||||
}
|
||||
|
||||
if (stream->passes > 1)
|
||||
{
|
||||
stream->outputCounts = 1;
|
||||
}
|
||||
else
|
||||
stream->outputCounts = extra_in_param->outputCounts;
|
||||
|
||||
if (extra_out_param)
|
||||
{
|
||||
if (stream->outputCounts == 0)
|
||||
extra_out_param->outputHeight = stream->out_param.outputHeight;
|
||||
else
|
||||
extra_out_param->outputHeight = std::min(stream->outputCounts, stream->out_param.outputHeight.value());
|
||||
extra_out_param->outputWidthByte = stream->out_param.outputWidthByte;
|
||||
}
|
||||
}
|
||||
|
||||
*out_param = stream->out_param;
|
||||
|
||||
return CELL_OK;
|
||||
@@ -557,145 +677,123 @@ s32 pngDecSetParameter(PStream stream, PInParam in_param, POutParam out_param, P
|
||||
|
||||
s32 pngDecodeData(ppu_thread& ppu, PHandle handle, PStream stream, vm::ptr<u8> data, PDataControlParam data_control_param, PDataOutInfo data_out_info, PCbControlDisp cb_control_disp = vm::null, PDispParam disp_param = vm::null)
|
||||
{
|
||||
if (cb_control_disp || disp_param)
|
||||
{
|
||||
fmt::throw_exception("Partial image decoding is not supported" HERE);
|
||||
}
|
||||
|
||||
// Indicate, that the PNG decoding is stopped/failed. This is incase, we return an error code in the middle of decoding
|
||||
data_out_info->status = CELL_PNGDEC_DEC_STATUS_STOP;
|
||||
|
||||
// Possibilities for decoding in different sizes aren't tested, so if anyone finds any of these cases, we'll know about it.
|
||||
if (stream->info.imageWidth != stream->out_param.outputWidth)
|
||||
{
|
||||
fmt::throw_exception("Image width doesn't match output width! (%d/%d)" HERE, stream->out_param.outputWidth, stream->info.imageWidth);
|
||||
}
|
||||
|
||||
if (stream->info.imageHeight != stream->out_param.outputHeight)
|
||||
{
|
||||
fmt::throw_exception("Image width doesn't match output height! (%d/%d)" HERE, stream->out_param.outputHeight, stream->info.imageHeight);
|
||||
}
|
||||
|
||||
// Get the amount of output bytes per line
|
||||
const u32 bytes_per_line = data_control_param->outputBytesPerLine;
|
||||
|
||||
// Whether to recaculate bytes per row
|
||||
bool recalculate_bytes_per_row = false;
|
||||
|
||||
// Check if the game is expecting the number of bytes per line to be lower, than the actual bytes per line on the image. (Arkedo Pixel for example)
|
||||
// In such case we strip the bit depth to be lower.
|
||||
if ((bytes_per_line < stream->out_param.outputWidthByte) && stream->out_param.outputBitDepth != 8)
|
||||
// Log this for now
|
||||
if (bytes_per_line < stream->out_param.outputWidthByte)
|
||||
{
|
||||
// Check if the packing is really 1 byte per 1 pixel
|
||||
if (stream->packing != CELL_PNGDEC_1BYTE_PER_1PIXEL)
|
||||
fmt::throw_exception("Bytes per line less than expected output! Got: %d, expected: %d" HERE, bytes_per_line, stream->out_param.outputWidthByte);
|
||||
}
|
||||
|
||||
// partial decoding
|
||||
if (cb_control_disp && stream->outputCounts > 0)
|
||||
{
|
||||
// get data from cb
|
||||
auto streamInfo = vm::ptr<CellPngDecStrmInfo>::make(handle->malloc_(ppu, sizeof(CellPngDecStrmInfo), handle->malloc_arg).addr());
|
||||
auto streamParam = vm::ptr<CellPngDecStrmParam>::make(handle->malloc_(ppu, sizeof(CellPngDecStrmParam), handle->malloc_arg).addr());
|
||||
stream->cbDispInfo = vm::ptr<CellPngDecDispInfo>::make(handle->malloc_(ppu, sizeof(CellPngDecDispInfo), handle->malloc_arg).addr());
|
||||
stream->cbDispParam = vm::ptr<CellPngDecDispParam>::make(handle->malloc_(ppu, sizeof(CellPngDecDispParam), handle->malloc_arg).addr());
|
||||
|
||||
auto freeMem = [&]()
|
||||
{
|
||||
fmt::throw_exception("Unexpected packing value! (%d)" HERE, stream->packing);
|
||||
}
|
||||
handle->free_(ppu, streamInfo, handle->free_arg);
|
||||
handle->free_(ppu, streamParam, handle->free_arg);
|
||||
handle->free_(ppu, stream->cbDispInfo, handle->free_arg);
|
||||
handle->free_(ppu, stream->cbDispParam, handle->free_arg);
|
||||
};
|
||||
|
||||
// Scale 16 bit depth down to 8 bit depth. PS3 uses png_set_strip_16, since png_set_scale_16 wasn't available back then.
|
||||
png_set_strip_16(stream->png_ptr);
|
||||
recalculate_bytes_per_row = true;
|
||||
}
|
||||
// Check if the outputWidthByte is smaller than the intended output length of a line. For example an image might be in RGB, but we need to output 4 components, so we need to perform alpha padding.
|
||||
else if (stream->out_param.outputWidthByte < (stream->out_param.outputWidth * stream->out_param.outputComponents))
|
||||
{
|
||||
// If fixed alpha is not specified in such a case, the default value for the alpha is 0xFF (255)
|
||||
if (!stream->fixed_alpha)
|
||||
// set things that won't change between callbacks
|
||||
stream->cbDispInfo->outputFrameWidthByte = bytes_per_line;
|
||||
stream->cbDispInfo->outputFrameHeight = stream->out_param.outputHeight;
|
||||
stream->cbDispInfo->outputWidthByte = stream->out_param.outputWidthByte;
|
||||
stream->cbDispInfo->outputBitDepth = stream->out_param.outputBitDepth;
|
||||
stream->cbDispInfo->outputComponents = stream->out_param.outputComponents;
|
||||
stream->cbDispInfo->outputHeight = stream->outputCounts;
|
||||
stream->cbDispInfo->outputStartXByte = 0;
|
||||
stream->cbDispInfo->outputStartY = 0;
|
||||
stream->cbDispInfo->scanPassCount = 0;
|
||||
stream->cbDispInfo->nextOutputStartY = 0;
|
||||
|
||||
stream->ppuContext = &ppu;
|
||||
stream->nextRow = stream->cbDispInfo->outputHeight;
|
||||
stream->cbCtrlDisp.cbCtrlDispArg = cb_control_disp->cbCtrlDispArg;
|
||||
stream->cbCtrlDisp.cbCtrlDispFunc = cb_control_disp->cbCtrlDispFunc;
|
||||
|
||||
stream->cbDispParam->nextOutputImage = disp_param->nextOutputImage;
|
||||
|
||||
streamInfo->decodedStrmSize = stream->buffer->cursor;
|
||||
// push the rest of the buffer we have
|
||||
if (stream->buffer->length > stream->buffer->cursor)
|
||||
{
|
||||
stream->fixed_alpha_colour = 0xFF;
|
||||
}
|
||||
|
||||
// We need to fill alpha (before or after, depending on the output colour format) using the fixed alpha value passed by the game.
|
||||
png_set_add_alpha(stream->png_ptr, stream->fixed_alpha_colour, stream->out_param.outputColorSpace == CELL_PNGDEC_RGBA ? PNG_FILLER_AFTER : PNG_FILLER_BEFORE);
|
||||
recalculate_bytes_per_row = true;
|
||||
}
|
||||
// We decode as RGBA, so we need to swap the alpha
|
||||
else if (stream->out_param.outputColorSpace == CELL_PNGDEC_ARGB)
|
||||
{
|
||||
// Swap the alpha channel for the ARGB output format, if the padding isn't needed
|
||||
png_set_swap_alpha(stream->png_ptr);
|
||||
}
|
||||
// Sometimes games pass in a RBG/RGBA image and want it as grayscale
|
||||
else if ((stream->out_param.outputColorSpace == CELL_PNGDEC_GRAYSCALE_ALPHA || stream->out_param.outputColorSpace == CELL_PNGDEC_GRAYSCALE)
|
||||
&& (stream->info.colorSpace == CELL_PNGDEC_RGB || stream->info.colorSpace == CELL_PNGDEC_RGBA))
|
||||
{
|
||||
// Tell libpng to convert it to grayscale
|
||||
png_set_rgb_to_gray(stream->png_ptr, PNG_ERROR_ACTION_NONE, PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT);
|
||||
recalculate_bytes_per_row = true;
|
||||
}
|
||||
|
||||
if (recalculate_bytes_per_row)
|
||||
{
|
||||
// Update the info structure
|
||||
png_read_update_info(stream->png_ptr, stream->info_ptr);
|
||||
|
||||
// Recalculate the bytes per row
|
||||
stream->out_param.outputWidthByte = png_get_rowbytes(stream->png_ptr, stream->info_ptr);
|
||||
}
|
||||
|
||||
// Calculate the image size
|
||||
u32 image_size = stream->out_param.outputWidthByte * stream->out_param.outputHeight;
|
||||
|
||||
// Buffer for storing the image
|
||||
std::vector<u8> png(image_size);
|
||||
|
||||
// Make an unique pointer for the row pointers
|
||||
std::vector<u8*> row_pointers(stream->out_param.outputHeight);
|
||||
|
||||
// Allocate memory for rows
|
||||
for (u32 y = 0; y < stream->out_param.outputHeight; y++)
|
||||
{
|
||||
row_pointers[y] = &png[y * stream->out_param.outputWidthByte];
|
||||
}
|
||||
|
||||
// Decode the image
|
||||
png_read_image(stream->png_ptr, row_pointers.data());
|
||||
|
||||
// Check if the image needs to be flipped
|
||||
const bool flip = stream->out_param.outputMode == CELL_PNGDEC_BOTTOM_TO_TOP;
|
||||
|
||||
// Copy the result to the output buffer
|
||||
switch (stream->out_param.outputColorSpace)
|
||||
{
|
||||
case CELL_PNGDEC_RGB:
|
||||
case CELL_PNGDEC_RGBA:
|
||||
case CELL_PNGDEC_ARGB:
|
||||
case CELL_PNGDEC_GRAYSCALE_ALPHA:
|
||||
{
|
||||
// Check if we need to flip the image or need to leave empty bytes at the end of a line
|
||||
if ((bytes_per_line > stream->out_param.outputWidthByte) || flip)
|
||||
{
|
||||
// Get how many bytes per line we need to output - bytesPerLine is total amount of bytes per line, rest is unused and the game can do as it pleases.
|
||||
const u32 line_size = std::min(bytes_per_line, stream->out_param.outputWidth * 4);
|
||||
|
||||
// If the game wants more bytes per line to be output, than the image has, then we simply copy what we have for each line,
|
||||
// and continue on the next line, thus leaving empty bytes at the end of the line.
|
||||
for (u32 i = 0; i < stream->out_param.outputHeight; i++)
|
||||
u8* data = static_cast<u8*>(stream->buffer->data.get_ptr()) + stream->buffer->cursor;
|
||||
try
|
||||
{
|
||||
const u32 dst = i * bytes_per_line;
|
||||
const u32 src = stream->out_param.outputWidth * 4 * (flip ? stream->out_param.outputHeight - i - 1 : i);
|
||||
memcpy(&data[dst], &png[src], line_size);
|
||||
png_process_data(stream->png_ptr, stream->info_ptr, data, stream->buffer->length - stream->buffer->cursor);
|
||||
}
|
||||
catch (LibPngCustomException&)
|
||||
{
|
||||
freeMem();
|
||||
return CELL_PNGDEC_ERROR_FATAL;
|
||||
}
|
||||
streamInfo->decodedStrmSize = stream->buffer->length;
|
||||
}
|
||||
|
||||
// todo: commandPtr
|
||||
// then just loop until the end, the callbacks should take care of the rest
|
||||
while (stream->endOfFile != true)
|
||||
{
|
||||
stream->cbCtrlStream.cbCtrlStrmFunc(ppu, streamInfo, streamParam, stream->cbCtrlStream.cbCtrlStrmArg);
|
||||
streamInfo->decodedStrmSize += streamParam->strmSize;
|
||||
try
|
||||
{
|
||||
png_process_data(stream->png_ptr, stream->info_ptr, static_cast<u8*>(streamParam->strmPtr.get_ptr()), streamParam->strmSize);
|
||||
}
|
||||
catch (LibPngCustomException&)
|
||||
{
|
||||
freeMem();
|
||||
return CELL_PNGDEC_ERROR_FATAL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We can simply copy the output to the data pointer specified by the game, since we already do alpha channel transformations in libpng, if needed
|
||||
memcpy(data.get_ptr(), png.data(), image_size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: fmt::throw_exception("Unsupported color space (%d)" HERE, stream->out_param.outputColorSpace);
|
||||
freeMem();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if the image needs to be flipped
|
||||
const bool flip = stream->out_param.outputMode == CELL_PNGDEC_BOTTOM_TO_TOP;
|
||||
|
||||
// Decode the image
|
||||
// todo: commandptr
|
||||
try
|
||||
{
|
||||
for (int j = 0; j < stream->passes; j++)
|
||||
{
|
||||
for (int i = 0; i < stream->out_param.outputHeight; ++i)
|
||||
{
|
||||
const u32 line = flip ? stream->out_param.outputHeight - i - 1 : i;
|
||||
png_read_row(stream->png_ptr, &data[line*bytes_per_line], nullptr);
|
||||
}
|
||||
}
|
||||
png_read_end(stream->png_ptr, stream->info_ptr);
|
||||
}
|
||||
catch (LibPngCustomException&)
|
||||
{
|
||||
return CELL_PNGDEC_ERROR_FATAL;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the number of iTXt, tEXt and zTXt chunks
|
||||
s32 text_chunks = 0;
|
||||
png_get_text(stream->png_ptr, stream->info_ptr, nullptr, &text_chunks);
|
||||
const s32 text_chunks = png_get_text(stream->png_ptr, stream->info_ptr, nullptr, nullptr);
|
||||
|
||||
// Set the chunk information and the previously obtained number of text chunks
|
||||
data_out_info->numText = (u32)text_chunks;
|
||||
data_out_info->chunkInformation = pngDecGetChunkInformation(stream, true);
|
||||
data_out_info->numUnknownChunk = 0; // TODO: Get this somehow. Does anything even use or need this?
|
||||
data_out_info->chunkInformation = pngDecGetChunkInformation(stream.get_ptr(), true);
|
||||
png_unknown_chunkp unknowns;
|
||||
const int num_unknowns = png_get_unknown_chunks(stream->png_ptr, stream->info_ptr, &unknowns);
|
||||
data_out_info->numUnknownChunk = num_unknowns;
|
||||
|
||||
// Indicate that the decoding succeeded
|
||||
data_out_info->status = CELL_PNGDEC_DEC_STATUS_FINISH;
|
||||
@@ -711,7 +809,7 @@ s32 cellPngDecCreate(ppu_thread& ppu, PPHandle handle, PThreadInParam threadInPa
|
||||
|
||||
s32 cellPngDecExtCreate(ppu_thread& ppu, PPHandle handle, PThreadInParam threadInParam, PThreadOutParam threadOutParam, PExtThreadInParam extThreadInParam, PExtThreadOutParam extThreadOutParam)
|
||||
{
|
||||
cellPngDec.warning("cellPngDecCreate(mainHandle=**0x%x, threadInParam=*0x%x, threadOutParam=*0x%x, extThreadInParam=*0x%x, extThreadOutParam=*0x%x)", handle, threadInParam, threadOutParam, extThreadInParam, extThreadOutParam);
|
||||
cellPngDec.warning("cellPngDecExtCreate(mainHandle=**0x%x, threadInParam=*0x%x, threadOutParam=*0x%x, extThreadInParam=*0x%x, extThreadOutParam=*0x%x)", handle, threadInParam, threadOutParam, extThreadInParam, extThreadOutParam);
|
||||
return pngDecCreate(ppu, handle, threadInParam, threadOutParam, extThreadInParam, extThreadOutParam);
|
||||
}
|
||||
|
||||
@@ -742,13 +840,51 @@ s32 cellPngDecClose(ppu_thread& ppu, PHandle handle, PStream stream)
|
||||
s32 cellPngDecReadHeader(PHandle handle, PStream stream, PInfo info)
|
||||
{
|
||||
cellPngDec.warning("cellPngDecReadHeader(handle=*0x%x, stream=*0x%x, info=*0x%x)", handle, stream, info);
|
||||
return pngReadHeader(stream, info);
|
||||
|
||||
// Read the header info
|
||||
png_read_info(stream->png_ptr, stream->info_ptr);
|
||||
|
||||
pngSetHeader(stream.get_ptr());
|
||||
|
||||
// Set the pointer to stream info
|
||||
*info = stream->info;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPngDecExtReadHeader(PHandle handle, PStream stream, PInfo info, PExtInfo extInfo)
|
||||
{
|
||||
cellPngDec.warning("cellPngDecExtReadHeader(handle=*0x%x, stream=*0x%x, info=*0x%x, extInfo=*0x%x)", handle, stream, info, extInfo);
|
||||
return pngReadHeader(stream, info, extInfo);
|
||||
// Set the reserved value to 0, if passed to the function. (Should this be arg error if they dont pass?)
|
||||
if (extInfo)
|
||||
{
|
||||
extInfo->reserved = 0;
|
||||
}
|
||||
|
||||
// lets push what we have so far
|
||||
u8* data = static_cast<u8*>(stream->buffer->data.get_ptr()) + stream->buffer->cursor;
|
||||
try
|
||||
{
|
||||
png_process_data(stream->png_ptr, stream->info_ptr, data, stream->buffer->length);
|
||||
}
|
||||
catch (LibPngCustomException&)
|
||||
{
|
||||
return CELL_PNGDEC_ERROR_HEADER;
|
||||
}
|
||||
|
||||
// lets hope we pushed enough for callback
|
||||
pngSetHeader(stream.get_ptr());
|
||||
|
||||
// png doesnt allow empty image, so quick check for 0 verifys if we got the header
|
||||
// not sure exactly what should happen if we dont have header, ask for more data with callback?
|
||||
if (stream->info.imageWidth == 0)
|
||||
{
|
||||
fmt::throw_exception("Invalid or not enough data sent to get header");
|
||||
return CELL_PNGDEC_ERROR_HEADER;
|
||||
}
|
||||
|
||||
// Set the pointer to stream info
|
||||
*info = stream->info;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellPngDecSetParameter(PHandle handle, PStream stream, PInParam inParam, POutParam outParam)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
namespace vm { using namespace ps3; }
|
||||
|
||||
#include "cellPng.h"
|
||||
@@ -264,9 +263,9 @@ struct CellPngDecCbCtrlDisp
|
||||
// Custom structs
|
||||
struct PngHandle
|
||||
{
|
||||
vm::ptr<CellPngDecCbControlMalloc> malloc;
|
||||
vm::ptr<CellPngDecCbControlMalloc> malloc_;
|
||||
vm::ptr<void> malloc_arg;
|
||||
vm::ptr<CellPngDecCbControlFree> free;
|
||||
vm::ptr<CellPngDecCbControlFree> free_;
|
||||
vm::ptr<void> free_arg;
|
||||
};
|
||||
|
||||
@@ -289,16 +288,22 @@ struct PngStream
|
||||
CellPngDecInfo info;
|
||||
CellPngDecOutParam out_param;
|
||||
CellPngDecSrc source;
|
||||
CellPngDecStrmInfo streamInfo;
|
||||
CellPngDecStrmParam streamParam;
|
||||
|
||||
// Fixed alpha value and flag
|
||||
bool fixed_alpha;
|
||||
be_t<u32> fixed_alpha_colour;
|
||||
// Partial decoding
|
||||
CellPngDecCbCtrlStrm cbCtrlStream;
|
||||
CellPngDecCbCtrlDisp cbCtrlDisp;
|
||||
vm::ptr<CellPngDecDispInfo> cbDispInfo;
|
||||
vm::ptr<CellPngDecDispParam> cbDispParam;
|
||||
ppu_thread* ppuContext;
|
||||
|
||||
u32 outputCounts = 0;
|
||||
u32 nextRow = 0;
|
||||
bool endOfFile = false;
|
||||
|
||||
// Pixel packing value
|
||||
be_t<s32> packing;
|
||||
|
||||
u32 passes;
|
||||
|
||||
// PNG custom read function structure, for decoding from a buffer
|
||||
vm::ptr<PngBuffer> buffer;
|
||||
|
||||
@@ -320,3 +325,24 @@ static s32 getPngDecColourType(u8 type)
|
||||
default: fmt::throw_exception("Unknown colour type: %d" HERE, type);
|
||||
}
|
||||
}
|
||||
|
||||
static bool cellPngColorSpaceHasAlpha(u32 colorspace)
|
||||
{
|
||||
switch (colorspace)
|
||||
{
|
||||
case CELL_PNGDEC_RGBA:
|
||||
case CELL_PNGDEC_ARGB:
|
||||
case CELL_PNGDEC_GRAYSCALE_ALPHA:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom exception for libPng errors
|
||||
|
||||
class LibPngCustomException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
LibPngCustomException(char const* const message) : runtime_error(message) {}
|
||||
};
|
||||
@@ -1032,6 +1032,8 @@ s32 cellSailPlayerUnregisterSource()
|
||||
|
||||
DECLARE(ppu_module_manager::cellSail)("cellSail", []()
|
||||
{
|
||||
static ppu_static_module cellSailAvi("cellSailAvi");
|
||||
|
||||
REG_FUNC(cellSail, cellSailMemAllocatorInitialize);
|
||||
|
||||
REG_FUNC(cellSail, cellSailFutureInitialize);
|
||||
|
||||
@@ -256,6 +256,9 @@ s32 cellSailRecorderDumpImage()
|
||||
|
||||
DECLARE(ppu_module_manager::cellSailRec)("cellSailRec", []()
|
||||
{
|
||||
static ppu_static_module cellMp4("cellMp4");
|
||||
static ppu_static_module cellApostSrcMini("cellApostSrcMini");
|
||||
|
||||
REG_FUNC(cellSailRec, cellSailProfileSetEsAudioParameter);
|
||||
REG_FUNC(cellSailRec, cellSailProfileSetEsVideoParameter);
|
||||
REG_FUNC(cellSailRec, cellSailProfileSetStreamParameter);
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
|
||||
logs::channel cellSaveData("cellSaveData", logs::level::notice);
|
||||
|
||||
SaveDialogBase::~SaveDialogBase()
|
||||
{
|
||||
}
|
||||
|
||||
// cellSaveData aliases (only for cellSaveData.cpp)
|
||||
using PSetList = vm::ptr<CellSaveDataSetList>;
|
||||
using PSetBuf = vm::ptr<CellSaveDataSetBuf>;
|
||||
@@ -572,6 +576,11 @@ static NEVER_INLINE s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version,
|
||||
case CELL_SAVEDATA_FILEOP_READ:
|
||||
{
|
||||
fs::file file(dir_path + file_path, fs::read);
|
||||
if (!file)
|
||||
{
|
||||
cellSaveData.error("savedata file not found");
|
||||
return CELL_SAVEDATA_ERROR_FAILURE;
|
||||
}
|
||||
file.seek(fileSet->fileOffset);
|
||||
fileGet->excSize = static_cast<u32>(file.read(fileSet->fileBuf.get_ptr(), std::min<u32>(fileSet->fileSize, fileSet->fileBufSize)));
|
||||
break;
|
||||
|
||||
@@ -292,7 +292,7 @@ struct SaveDataEntry
|
||||
class SaveDialogBase
|
||||
{
|
||||
public:
|
||||
virtual ~SaveDialogBase() = default;
|
||||
virtual ~SaveDialogBase();
|
||||
|
||||
virtual s32 ShowSaveDataList(std::vector<SaveDataEntry>& save_entries, s32 focused, vm::ptr<CellSaveDataListSet> listSet) = 0;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
namespace vm { using namespace ps3; }
|
||||
|
||||
logs::channel cellSpudll("cellSpudll", logs::level::notice);
|
||||
|
||||
s32 cellSpudllGetImageSize(vm::ptr<u32> psize, vm::cptr<void> so_elf, vm::cptr<struct CellSpudllHandleConfig> config)
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
logs::channel cellSpurs("cellSpurs", logs::level::notice);
|
||||
|
||||
s32 sys_spu_image_close(vm::ptr<sys_spu_image_t> img);
|
||||
|
||||
// TODO
|
||||
struct cell_error_t
|
||||
{
|
||||
@@ -82,7 +84,7 @@ namespace _spurs
|
||||
s32 add_default_syswkl(vm::ptr<CellSpurs> spurs, vm::cptr<u8> swlPriority, u32 swlMaxSpu, u32 swlIsPreem);
|
||||
|
||||
// Destroy the SPURS SPU threads and thread group
|
||||
s32 finalize_spu(vm::ptr<CellSpurs> spurs);
|
||||
s32 finalize_spu(ppu_thread&, vm::ptr<CellSpurs> spurs);
|
||||
|
||||
// Stop the event helper thread
|
||||
s32 stop_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs);
|
||||
@@ -274,7 +276,7 @@ namespace _spurs
|
||||
//s32 cellSpursEventFlagWait(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode);
|
||||
//s32 cellSpursEventFlagTryWait(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode);
|
||||
//s32 cellSpursEventFlagAttachLv2EventQueue(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag);
|
||||
//s32 cellSpursEventFlagDetachLv2EventQueue(vm::ptr<CellSpursEventFlag> eventFlag);
|
||||
//s32 cellSpursEventFlagDetachLv2EventQueue(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag);
|
||||
//s32 cellSpursEventFlagGetDirection(vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u32> direction);
|
||||
//s32 cellSpursEventFlagGetClearMode(vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u32> clear_mode);
|
||||
//s32 cellSpursEventFlagGetTasksetAddress(vm::ptr<CellSpursEventFlag> eventFlag, vm::pptr<CellSpursTaskset> taskset);
|
||||
@@ -378,7 +380,7 @@ s32 _spurs::create_lv2_eq(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<u32
|
||||
|
||||
if (s32 rc = _spurs::attach_lv2_eq(ppu, spurs, *queueId, port, 1, true))
|
||||
{
|
||||
sys_event_queue_destroy(*queueId, SYS_EVENT_QUEUE_DESTROY_FORCE);
|
||||
sys_event_queue_destroy(ppu, *queueId, SYS_EVENT_QUEUE_DESTROY_FORCE);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
@@ -487,8 +489,6 @@ void _spurs::handler_wait_ready(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
|
||||
|
||||
while (true)
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
|
||||
if (spurs->handlerExiting)
|
||||
{
|
||||
CHECK_SUCCESS(CALL_FUNC(ppu, sys_lwmutex_unlock, ppu, spurs.ptr(&CellSpurs::mutex)));
|
||||
@@ -567,16 +567,14 @@ void _spurs::handler_entry(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
|
||||
|
||||
while (true)
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
|
||||
if (spurs->flags1 & SF1_EXIT_IF_NO_WORK)
|
||||
{
|
||||
_spurs::handler_wait_ready(ppu, spurs);
|
||||
}
|
||||
|
||||
CHECK_SUCCESS(sys_spu_thread_group_start(spurs->spuTG));
|
||||
CHECK_SUCCESS(sys_spu_thread_group_start(ppu, spurs->spuTG));
|
||||
|
||||
if (s32 rc = sys_spu_thread_group_join(spurs->spuTG, vm::null, vm::null))
|
||||
if (s32 rc = sys_spu_thread_group_join(ppu, spurs->spuTG, vm::null, vm::null))
|
||||
{
|
||||
if (rc == CELL_ESTAT)
|
||||
{
|
||||
@@ -676,7 +674,7 @@ s32 _spurs::wakeup_shutdown_completion_waiter(ppu_thread& ppu, vm::ptr<CellSpurs
|
||||
if (!wklF->hook || wklEvent->load() & 0x10)
|
||||
{
|
||||
verify(HERE), (wklF->x28 == 2);
|
||||
rc = sys_semaphore_post((u32)wklF->sem, 1);
|
||||
rc = sys_semaphore_post(ppu, (u32)wklF->sem, 1);
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -714,11 +712,11 @@ void _spurs::event_helper_entry(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
|
||||
|
||||
for (u32 i = 0; i < CELL_SPURS_MAX_WORKLOAD; i++)
|
||||
{
|
||||
sys_semaphore_post((u32)spurs->wklF1[i].sem, 1);
|
||||
sys_semaphore_post(ppu, (u32)spurs->wklF1[i].sem, 1);
|
||||
|
||||
if (spurs->flags1 & SF1_32_WORKLOADS)
|
||||
{
|
||||
sys_semaphore_post((u32)spurs->wklF2[i].sem, 1);
|
||||
sys_semaphore_post(ppu, (u32)spurs->wklF2[i].sem, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -749,7 +747,7 @@ void _spurs::event_helper_entry(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
|
||||
}
|
||||
else if (data0 == 2)
|
||||
{
|
||||
CHECK_SUCCESS(sys_semaphore_post((u32)spurs->semPrv, 1));
|
||||
CHECK_SUCCESS(sys_semaphore_post(ppu, (u32)spurs->semPrv, 1));
|
||||
}
|
||||
else if (data0 == 3)
|
||||
{
|
||||
@@ -777,7 +775,7 @@ s32 _spurs::create_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 p
|
||||
return CELL_SPURS_CORE_ERROR_AGAIN;
|
||||
}
|
||||
|
||||
sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE);
|
||||
sys_event_queue_destroy(ppu, spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE);
|
||||
return CELL_SPURS_CORE_ERROR_AGAIN;
|
||||
}
|
||||
|
||||
@@ -790,7 +788,7 @@ s32 _spurs::create_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 p
|
||||
return CELL_SPURS_CORE_ERROR_STAT;
|
||||
}
|
||||
|
||||
sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE);
|
||||
sys_event_queue_destroy(ppu, spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE);
|
||||
return CELL_SPURS_CORE_ERROR_STAT;
|
||||
}
|
||||
|
||||
@@ -816,7 +814,7 @@ s32 _spurs::create_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 p
|
||||
return CELL_SPURS_CORE_ERROR_STAT;
|
||||
}
|
||||
|
||||
sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE);
|
||||
sys_event_queue_destroy(ppu, spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE);
|
||||
return CELL_SPURS_CORE_ERROR_STAT;
|
||||
}
|
||||
|
||||
@@ -841,13 +839,13 @@ s32 _spurs::add_default_syswkl(vm::ptr<CellSpurs> spurs, vm::cptr<u8> swlPriorit
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 _spurs::finalize_spu(vm::ptr<CellSpurs> spurs)
|
||||
s32 _spurs::finalize_spu(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
|
||||
{
|
||||
if (spurs->flags & SAF_UNKNOWN_FLAG_7 || spurs->flags & SAF_UNKNOWN_FLAG_8)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
CHECK_SUCCESS(sys_spu_thread_group_join(spurs->spuTG, vm::null, vm::null));
|
||||
CHECK_SUCCESS(sys_spu_thread_group_join(ppu, spurs->spuTG, vm::null, vm::null));
|
||||
|
||||
if (s32 rc = sys_spu_thread_group_destroy(spurs->spuTG))
|
||||
{
|
||||
@@ -882,7 +880,7 @@ s32 _spurs::stop_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
|
||||
return CELL_SPURS_CORE_ERROR_STAT;
|
||||
}
|
||||
|
||||
if (sys_event_port_send(spurs->eventPort, 0, 1, 0) != CELL_OK)
|
||||
if (sys_event_port_send(ppu, spurs->eventPort, 0, 1, 0) != CELL_OK)
|
||||
{
|
||||
return CELL_SPURS_CORE_ERROR_STAT;
|
||||
}
|
||||
@@ -897,7 +895,7 @@ s32 _spurs::stop_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
|
||||
CHECK_SUCCESS(sys_event_port_disconnect(spurs->eventPort));
|
||||
CHECK_SUCCESS(sys_event_port_destroy(spurs->eventPort));
|
||||
CHECK_SUCCESS(_spurs::detach_lv2_eq(spurs, spurs->spuPort, true));
|
||||
CHECK_SUCCESS(sys_event_queue_destroy(spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE));
|
||||
CHECK_SUCCESS(sys_event_queue_destroy(ppu, spurs->eventQueue, SYS_EVENT_QUEUE_DESTROY_FORCE));
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -1120,9 +1118,10 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision,
|
||||
}
|
||||
|
||||
// entry point cannot be initialized immediately because SPU LS will be rewritten by sys_spu_thread_group_start()
|
||||
idm::get<SPUThread>(spurs->spus[num])->custom_task = [entry = spurs->spuImg.entry_point](SPUThread& spu)
|
||||
//idm::get<SPUThread>(spurs->spus[num])->custom_task = [entry = spurs->spuImg.entry_point](SPUThread& spu)
|
||||
{
|
||||
spu.RegisterHleFunction(entry, spursKernelEntry);
|
||||
// Disabled
|
||||
//spu.RegisterHleFunction(entry, spursKernelEntry);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1143,7 +1142,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision,
|
||||
// Create a mutex to protect access to SPURS handler thread data
|
||||
if (s32 rc = sys_lwmutex_create(lwMutex, vm::make_var(sys_lwmutex_attribute_t{ SYS_SYNC_PRIORITY, SYS_SYNC_NOT_RECURSIVE, "_spuPrv" })))
|
||||
{
|
||||
_spurs::finalize_spu(spurs);
|
||||
_spurs::finalize_spu(ppu, spurs);
|
||||
return rollback(), rc;
|
||||
}
|
||||
|
||||
@@ -1151,7 +1150,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision,
|
||||
if (s32 rc = sys_lwcond_create(lwCond, lwMutex, vm::make_var(sys_lwcond_attribute_t{ "_spuPrv" })))
|
||||
{
|
||||
sys_lwmutex_destroy(ppu, lwMutex);
|
||||
_spurs::finalize_spu(spurs);
|
||||
_spurs::finalize_spu(ppu, spurs);
|
||||
return rollback(), rc;
|
||||
}
|
||||
|
||||
@@ -1168,7 +1167,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision,
|
||||
{
|
||||
sys_lwcond_destroy(lwCond);
|
||||
sys_lwmutex_destroy(ppu, lwMutex);
|
||||
_spurs::finalize_spu(spurs);
|
||||
_spurs::finalize_spu(ppu, spurs);
|
||||
return rollback(), rc;
|
||||
}
|
||||
|
||||
@@ -1178,7 +1177,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision,
|
||||
_spurs::stop_event_helper(ppu, spurs);
|
||||
sys_lwcond_destroy(lwCond);
|
||||
sys_lwmutex_destroy(ppu, lwMutex);
|
||||
_spurs::finalize_spu(spurs);
|
||||
_spurs::finalize_spu(ppu, spurs);
|
||||
return rollback(), rc;
|
||||
}
|
||||
|
||||
@@ -1190,7 +1189,7 @@ s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision,
|
||||
_spurs::stop_event_helper(ppu, spurs);
|
||||
sys_lwcond_destroy(lwCond);
|
||||
sys_lwmutex_destroy(ppu, lwMutex);
|
||||
_spurs::finalize_spu(spurs);
|
||||
_spurs::finalize_spu(ppu, spurs);
|
||||
return rollback(), rc;
|
||||
}
|
||||
|
||||
@@ -2789,7 +2788,7 @@ s32 cellSpursEventFlagSet(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag
|
||||
// Signal the PPU thread to be woken up
|
||||
eventFlag->pendingRecvTaskEvents[ppuWaitSlot] = ppuEvents;
|
||||
|
||||
CHECK_SUCCESS(sys_event_port_send(eventFlag->eventPortId, 0, 0, 0));
|
||||
CHECK_SUCCESS(sys_event_port_send(ppu, eventFlag->eventPortId, 0, 0, 0));
|
||||
}
|
||||
|
||||
if (pendingRecv)
|
||||
@@ -3069,7 +3068,7 @@ s32 cellSpursEventFlagAttachLv2EventQueue(ppu_thread& ppu, vm::ptr<CellSpursEven
|
||||
|
||||
if (_spurs::detach_lv2_eq(spurs, *port, true) == CELL_OK)
|
||||
{
|
||||
sys_event_queue_destroy(*eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE);
|
||||
sys_event_queue_destroy(ppu, *eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE);
|
||||
}
|
||||
|
||||
return failure(rc);
|
||||
@@ -3079,7 +3078,7 @@ s32 cellSpursEventFlagAttachLv2EventQueue(ppu_thread& ppu, vm::ptr<CellSpursEven
|
||||
}
|
||||
|
||||
/// Detach an LV2 event queue from SPURS event flag
|
||||
s32 cellSpursEventFlagDetachLv2EventQueue(vm::ptr<CellSpursEventFlag> eventFlag)
|
||||
s32 cellSpursEventFlagDetachLv2EventQueue(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag)
|
||||
{
|
||||
cellSpurs.warning("cellSpursEventFlagDetachLv2EventQueue(eventFlag=*0x%x)", eventFlag);
|
||||
|
||||
@@ -3133,7 +3132,7 @@ s32 cellSpursEventFlagDetachLv2EventQueue(vm::ptr<CellSpursEventFlag> eventFlag)
|
||||
|
||||
if (rc == CELL_OK)
|
||||
{
|
||||
rc = sys_event_queue_destroy(eventFlag->eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE);
|
||||
rc = sys_event_queue_destroy(ppu, eventFlag->eventQueueId, SYS_EVENT_QUEUE_DESTROY_FORCE);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "cellSpurs.h"
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Externs
|
||||
@@ -265,10 +266,10 @@ bool spursKernel1SelectWorkload(SPUThread& spu)
|
||||
u32 wklSelectedId;
|
||||
u32 pollStatus;
|
||||
|
||||
vm::reservation_op(vm::cast(ctxt->spurs.addr(), HERE), 128, [&]()
|
||||
//vm::reservation_op(vm::cast(ctxt->spurs.addr(), HERE), 128, [&]()
|
||||
{
|
||||
// lock the first 0x80 bytes of spurs
|
||||
auto spurs = ctxt->spurs.get_ptr_priv();
|
||||
auto spurs = ctxt->spurs.get_ptr();
|
||||
|
||||
// Calculate the contention (number of SPUs used) for each workload
|
||||
u8 contention[CELL_SPURS_MAX_WORKLOAD];
|
||||
@@ -428,7 +429,7 @@ bool spursKernel1SelectWorkload(SPUThread& spu)
|
||||
}
|
||||
|
||||
std::memcpy(vm::base(spu.offset + 0x100), spurs, 128);
|
||||
});
|
||||
}//);
|
||||
|
||||
u64 result = (u64)wklSelectedId << 32;
|
||||
result |= pollStatus;
|
||||
@@ -449,10 +450,10 @@ bool spursKernel2SelectWorkload(SPUThread& spu)
|
||||
u32 wklSelectedId;
|
||||
u32 pollStatus;
|
||||
|
||||
vm::reservation_op(vm::cast(ctxt->spurs.addr(), HERE), 128, [&]()
|
||||
//vm::reservation_op(vm::cast(ctxt->spurs.addr(), HERE), 128, [&]()
|
||||
{
|
||||
// lock the first 0x80 bytes of spurs
|
||||
auto spurs = ctxt->spurs.get_ptr_priv();
|
||||
auto spurs = ctxt->spurs.get_ptr();
|
||||
|
||||
// Calculate the contention (number of SPUs used) for each workload
|
||||
u8 contention[CELL_SPURS_MAX_WORKLOAD2];
|
||||
@@ -601,7 +602,7 @@ bool spursKernel2SelectWorkload(SPUThread& spu)
|
||||
}
|
||||
|
||||
std::memcpy(vm::base(spu.offset + 0x100), spurs, 128);
|
||||
});
|
||||
}//);
|
||||
|
||||
u64 result = (u64)wklSelectedId << 32;
|
||||
result |= pollStatus;
|
||||
@@ -632,10 +633,10 @@ void spursKernelDispatchWorkload(SPUThread& spu, u64 widAndPollStatus)
|
||||
switch (wklInfo->addr.addr())
|
||||
{
|
||||
case SPURS_IMG_ADDR_SYS_SRV_WORKLOAD:
|
||||
spu.RegisterHleFunction(0xA00, spursSysServiceEntry);
|
||||
//spu.RegisterHleFunction(0xA00, spursSysServiceEntry);
|
||||
break;
|
||||
case SPURS_IMG_ADDR_TASKSET_PM:
|
||||
spu.RegisterHleFunction(0xA00, spursTasksetEntry);
|
||||
//spu.RegisterHleFunction(0xA00, spursTasksetEntry);
|
||||
break;
|
||||
default:
|
||||
std::memcpy(vm::base(spu.offset + 0xA00), wklInfo->addr.get_ptr(), wklInfo->size);
|
||||
@@ -685,11 +686,7 @@ bool spursKernelWorkloadExit(SPUThread& spu)
|
||||
// SPURS kernel entry point
|
||||
bool spursKernelEntry(SPUThread& spu)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
std::this_thread::sleep_for(100ms);
|
||||
CHECK_EMU_STATUS;
|
||||
}
|
||||
thread_ctrl::eternalize();
|
||||
|
||||
auto ctxt = vm::_ptr<SpursKernelContext>(spu.offset + 0x100);
|
||||
memset(ctxt, 0, sizeof(SpursKernelContext));
|
||||
@@ -724,10 +721,10 @@ bool spursKernelEntry(SPUThread& spu)
|
||||
}
|
||||
|
||||
// Register SPURS kernel HLE functions
|
||||
spu.UnregisterHleFunctions(0, 0x40000/*LS_BOTTOM*/);
|
||||
spu.RegisterHleFunction(isKernel2 ? CELL_SPURS_KERNEL2_ENTRY_ADDR : CELL_SPURS_KERNEL1_ENTRY_ADDR, spursKernelEntry);
|
||||
spu.RegisterHleFunction(ctxt->exitToKernelAddr, spursKernelWorkloadExit);
|
||||
spu.RegisterHleFunction(ctxt->selectWorkloadAddr, isKernel2 ? spursKernel2SelectWorkload : spursKernel1SelectWorkload);
|
||||
//spu.UnregisterHleFunctions(0, 0x40000/*LS_BOTTOM*/);
|
||||
//spu.RegisterHleFunction(isKernel2 ? CELL_SPURS_KERNEL2_ENTRY_ADDR : CELL_SPURS_KERNEL1_ENTRY_ADDR, spursKernelEntry);
|
||||
//spu.RegisterHleFunction(ctxt->exitToKernelAddr, spursKernelWorkloadExit);
|
||||
//spu.RegisterHleFunction(ctxt->selectWorkloadAddr, isKernel2 ? spursKernel2SelectWorkload : spursKernel1SelectWorkload);
|
||||
|
||||
// Start the system service
|
||||
spursKernelDispatchWorkload(spu, ((u64)CELL_SPURS_SYS_SERVICE_WORKLOAD_ID) << 32);
|
||||
@@ -772,11 +769,9 @@ void spursSysServiceIdleHandler(SPUThread& spu, SpursKernelContext* ctxt)
|
||||
{
|
||||
bool shouldExit;
|
||||
|
||||
std::unique_lock<named_thread> lock(spu, std::defer_lock);
|
||||
|
||||
while (true)
|
||||
{
|
||||
vm::reservation_acquire(vm::base(spu.offset + 0x100), vm::cast(ctxt->spurs.addr(), HERE), 128);
|
||||
//vm::reservation_acquire(vm::base(spu.offset + 0x100), vm::cast(ctxt->spurs.addr(), HERE), 128);
|
||||
auto spurs = vm::_ptr<CellSpurs>(spu.offset + 0x100);
|
||||
|
||||
// Find the number of SPUs that are idling in this SPURS instance
|
||||
@@ -861,13 +856,11 @@ void spursSysServiceIdleHandler(SPUThread& spu, SpursKernelContext* ctxt)
|
||||
if (spuIdling && shouldExit == false && foundReadyWorkload == false)
|
||||
{
|
||||
// The system service blocks by making a reservation and waiting on the lock line reservation lost event.
|
||||
CHECK_EMU_STATUS;
|
||||
if (!lock) { lock.lock(); continue; }
|
||||
thread_ctrl::wait_for(1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vm::reservation_update(vm::cast(ctxt->spurs.addr(), HERE), vm::base(spu.offset + 0x100), 128) && (shouldExit || foundReadyWorkload))
|
||||
//if (vm::reservation_update(vm::cast(ctxt->spurs.addr(), HERE), vm::base(spu.offset + 0x100), 128) && (shouldExit || foundReadyWorkload))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -895,11 +888,11 @@ void spursSysServiceMain(SPUThread& spu, u32 pollStatus)
|
||||
{
|
||||
ctxt->sysSrvInitialised = 1;
|
||||
|
||||
vm::reservation_acquire(vm::base(spu.offset + 0x100), vm::cast(ctxt->spurs.addr(), HERE), 128);
|
||||
//vm::reservation_acquire(vm::base(spu.offset + 0x100), vm::cast(ctxt->spurs.addr(), HERE), 128);
|
||||
|
||||
vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
|
||||
//vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
|
||||
{
|
||||
auto spurs = ctxt->spurs.get_ptr_priv();
|
||||
auto spurs = ctxt->spurs.get_ptr();
|
||||
|
||||
// Halt if already initialised
|
||||
if (spurs->sysSrvOnSpu & (1 << ctxt->spuNum))
|
||||
@@ -911,7 +904,7 @@ void spursSysServiceMain(SPUThread& spu, u32 pollStatus)
|
||||
spurs->sysSrvOnSpu |= 1 << ctxt->spuNum;
|
||||
|
||||
std::memcpy(vm::base(spu.offset + 0x2D80), spurs->wklState1, 128);
|
||||
});
|
||||
}//);
|
||||
|
||||
ctxt->traceBuffer = 0;
|
||||
ctxt->traceMsgCount = -1;
|
||||
@@ -937,7 +930,6 @@ void spursSysServiceMain(SPUThread& spu, u32 pollStatus)
|
||||
|
||||
while (true)
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
// Process requests for the system service
|
||||
spursSysServiceProcessRequests(spu, ctxt);
|
||||
|
||||
@@ -980,7 +972,6 @@ void spursSysServiceMain(SPUThread& spu, u32 pollStatus)
|
||||
cellSpursModulePutTrace(&pkt, ctxt->dmaTagId);
|
||||
|
||||
spursSysServiceIdleHandler(spu, ctxt);
|
||||
CHECK_EMU_STATUS;
|
||||
|
||||
goto poll;
|
||||
}
|
||||
@@ -993,9 +984,9 @@ void spursSysServiceProcessRequests(SPUThread& spu, SpursKernelContext* ctxt)
|
||||
bool updateWorkload = false;
|
||||
bool terminate = false;
|
||||
|
||||
vm::reservation_op(vm::cast(ctxt->spurs.addr() + OFFSET_32(CellSpurs, wklState1), HERE), 128, [&]()
|
||||
//vm::reservation_op(vm::cast(ctxt->spurs.addr() + OFFSET_32(CellSpurs, wklState1), HERE), 128, [&]()
|
||||
{
|
||||
auto spurs = ctxt->spurs.get_ptr_priv();
|
||||
auto spurs = ctxt->spurs.get_ptr();
|
||||
|
||||
// Terminate request
|
||||
if (spurs->sysSrvMsgTerminate & (1 << ctxt->spuNum))
|
||||
@@ -1018,7 +1009,7 @@ void spursSysServiceProcessRequests(SPUThread& spu, SpursKernelContext* ctxt)
|
||||
}
|
||||
|
||||
std::memcpy(vm::base(spu.offset + 0x2D80), spurs->wklState1, 128);
|
||||
});
|
||||
}//);
|
||||
|
||||
// Process update workload message
|
||||
if (updateWorkload)
|
||||
@@ -1072,9 +1063,9 @@ void spursSysServiceActivateWorkload(SPUThread& spu, SpursKernelContext* ctxt)
|
||||
}
|
||||
}
|
||||
|
||||
vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
|
||||
//vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
|
||||
{
|
||||
auto spurs = ctxt->spurs.get_ptr_priv();
|
||||
auto spurs = ctxt->spurs.get_ptr();
|
||||
|
||||
for (u32 i = 0; i < CELL_SPURS_MAX_WORKLOAD; i++)
|
||||
{
|
||||
@@ -1129,7 +1120,7 @@ void spursSysServiceActivateWorkload(SPUThread& spu, SpursKernelContext* ctxt)
|
||||
}
|
||||
|
||||
std::memcpy(vm::base(spu.offset + 0x2D80), spurs->wklState1, 128);
|
||||
});
|
||||
}//);
|
||||
|
||||
if (wklShutdownBitSet)
|
||||
{
|
||||
@@ -1144,9 +1135,9 @@ void spursSysServiceUpdateShutdownCompletionEvents(SPUThread& spu, SpursKernelCo
|
||||
// workloads that have a shutdown completion hook registered
|
||||
u32 wklNotifyBitSet;
|
||||
u8 spuPort;
|
||||
vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
|
||||
//vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
|
||||
{
|
||||
auto spurs = ctxt->spurs.get_ptr_priv();
|
||||
auto spurs = ctxt->spurs.get_ptr();
|
||||
|
||||
wklNotifyBitSet = 0;
|
||||
spuPort = spurs->spuPort;;
|
||||
@@ -1172,7 +1163,7 @@ void spursSysServiceUpdateShutdownCompletionEvents(SPUThread& spu, SpursKernelCo
|
||||
}
|
||||
|
||||
std::memcpy(vm::base(spu.offset + 0x2D80), spurs->wklState1, 128);
|
||||
});
|
||||
}//);
|
||||
|
||||
if (wklNotifyBitSet)
|
||||
{
|
||||
@@ -1196,9 +1187,9 @@ void spursSysServiceTraceUpdate(SPUThread& spu, SpursKernelContext* ctxt, u32 ar
|
||||
bool notify;
|
||||
|
||||
u8 sysSrvMsgUpdateTrace;
|
||||
vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
|
||||
//vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
|
||||
{
|
||||
auto spurs = ctxt->spurs.get_ptr_priv();
|
||||
auto spurs = ctxt->spurs.get_ptr();
|
||||
auto& trace = spurs->sysSrvTrace.raw();
|
||||
|
||||
sysSrvMsgUpdateTrace = trace.sysSrvMsgUpdateTrace;
|
||||
@@ -1220,12 +1211,12 @@ void spursSysServiceTraceUpdate(SPUThread& spu, SpursKernelContext* ctxt, u32 ar
|
||||
}
|
||||
|
||||
std::memcpy(vm::base(spu.offset + 0x2D80), spurs->wklState1, 128);
|
||||
});
|
||||
}//);
|
||||
|
||||
// Get trace parameters from CellSpurs and store them in the LS
|
||||
if (((sysSrvMsgUpdateTrace & (1 << ctxt->spuNum)) != 0) || (arg3 != 0))
|
||||
{
|
||||
vm::reservation_acquire(vm::base(spu.offset + 0x80), ctxt->spurs.ptr(&CellSpurs::traceBuffer).addr(), 128);
|
||||
//vm::reservation_acquire(vm::base(spu.offset + 0x80), ctxt->spurs.ptr(&CellSpurs::traceBuffer).addr(), 128);
|
||||
auto spurs = vm::_ptr<CellSpurs>(spu.offset + 0x80 - OFFSET_32(CellSpurs, traceBuffer));
|
||||
|
||||
if (ctxt->traceMsgCount != 0xFF || spurs->traceBuffer.addr() == 0)
|
||||
@@ -1261,9 +1252,9 @@ void spursSysServiceCleanupAfterSystemWorkload(SPUThread& spu, SpursKernelContex
|
||||
|
||||
bool do_return = false;
|
||||
|
||||
vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
|
||||
//vm::reservation_op(ctxt->spurs.ptr(&CellSpurs::wklState1).addr(), 128, [&]()
|
||||
{
|
||||
auto spurs = ctxt->spurs.get_ptr_priv();
|
||||
auto spurs = ctxt->spurs.get_ptr();
|
||||
|
||||
if (spurs->sysSrvPreemptWklId[ctxt->spuNum] == 0xFF)
|
||||
{
|
||||
@@ -1275,15 +1266,15 @@ void spursSysServiceCleanupAfterSystemWorkload(SPUThread& spu, SpursKernelContex
|
||||
spurs->sysSrvPreemptWklId[ctxt->spuNum] = 0xFF;
|
||||
|
||||
std::memcpy(vm::base(spu.offset + 0x2D80), spurs->wklState1, 128);
|
||||
});
|
||||
}//);
|
||||
|
||||
if (do_return) return;
|
||||
|
||||
spursSysServiceActivateWorkload(spu, ctxt);
|
||||
|
||||
vm::reservation_op(vm::cast(ctxt->spurs.addr(), HERE), 128, [&]()
|
||||
//vm::reservation_op(vm::cast(ctxt->spurs.addr(), HERE), 128, [&]()
|
||||
{
|
||||
auto spurs = ctxt->spurs.get_ptr_priv();
|
||||
auto spurs = ctxt->spurs.get_ptr();
|
||||
|
||||
if (wklId >= CELL_SPURS_MAX_WORKLOAD)
|
||||
{
|
||||
@@ -1297,7 +1288,7 @@ void spursSysServiceCleanupAfterSystemWorkload(SPUThread& spu, SpursKernelContex
|
||||
}
|
||||
|
||||
std::memcpy(vm::base(spu.offset + 0x100), spurs, 128);
|
||||
});
|
||||
}//);
|
||||
|
||||
// Set the current workload id to the id of the pre-empted workload since cellSpursModulePutTrace
|
||||
// uses the current worload id to determine the workload to which the trace belongs
|
||||
@@ -1350,9 +1341,9 @@ bool spursTasksetEntry(SPUThread& spu)
|
||||
ctxt->taskId = 0xFFFFFFFF;
|
||||
|
||||
// Register SPURS takset policy module HLE functions
|
||||
spu.UnregisterHleFunctions(CELL_SPURS_TASKSET_PM_ENTRY_ADDR, 0x40000/*LS_BOTTOM*/);
|
||||
spu.RegisterHleFunction(CELL_SPURS_TASKSET_PM_ENTRY_ADDR, spursTasksetEntry);
|
||||
spu.RegisterHleFunction(ctxt->syscallAddr, spursTasksetSyscallEntry);
|
||||
//spu.UnregisterHleFunctions(CELL_SPURS_TASKSET_PM_ENTRY_ADDR, 0x40000/*LS_BOTTOM*/);
|
||||
//spu.RegisterHleFunction(CELL_SPURS_TASKSET_PM_ENTRY_ADDR, spursTasksetEntry);
|
||||
//spu.RegisterHleFunction(ctxt->syscallAddr, spursTasksetSyscallEntry);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -1442,9 +1433,9 @@ s32 spursTasksetProcessRequest(SPUThread& spu, s32 request, u32* taskId, u32* is
|
||||
|
||||
s32 rc = CELL_OK;
|
||||
s32 numNewlyReadyTasks;
|
||||
vm::reservation_op(vm::cast(ctxt->taskset.addr(), HERE), 128, [&]()
|
||||
//vm::reservation_op(vm::cast(ctxt->taskset.addr(), HERE), 128, [&]()
|
||||
{
|
||||
auto taskset = ctxt->taskset.get_ptr_priv();
|
||||
auto taskset = ctxt->taskset.get_ptr();
|
||||
|
||||
// Verify taskset state is valid
|
||||
be_t<v128> _0(v128::from32(0));
|
||||
@@ -1601,12 +1592,12 @@ s32 spursTasksetProcessRequest(SPUThread& spu, s32 request, u32* taskId, u32* is
|
||||
taskset->ready = ready;
|
||||
|
||||
std::memcpy(vm::base(spu.offset + 0x2700), taskset, 128);
|
||||
});
|
||||
}//);
|
||||
|
||||
// Increment the ready count of the workload by the number of tasks that have become ready
|
||||
vm::reservation_op(vm::cast(kernelCtxt->spurs.addr(), HERE), 128, [&]()
|
||||
//vm::reservation_op(vm::cast(kernelCtxt->spurs.addr(), HERE), 128, [&]()
|
||||
{
|
||||
auto spurs = kernelCtxt->spurs.get_ptr_priv();
|
||||
auto spurs = kernelCtxt->spurs.get_ptr();
|
||||
|
||||
s32 readyCount = kernelCtxt->wklCurrentId < CELL_SPURS_MAX_WORKLOAD ? spurs->wklReadyCount1[kernelCtxt->wklCurrentId].load() : spurs->wklIdleSpuCountOrReadyCount2[kernelCtxt->wklCurrentId & 0x0F].load();
|
||||
readyCount += numNewlyReadyTasks;
|
||||
@@ -1622,7 +1613,7 @@ s32 spursTasksetProcessRequest(SPUThread& spu, s32 request, u32* taskId, u32* is
|
||||
}
|
||||
|
||||
std::memcpy(vm::base(spu.offset + 0x100), spurs, 128);
|
||||
});
|
||||
}//);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -6,10 +6,6 @@
|
||||
#include "Emu/Cell/lv2/sys_process.h"
|
||||
#include "cellSync.h"
|
||||
|
||||
#include "Emu/Memory/wait_engine.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
logs::channel cellSync("cellSync", logs::level::notice);
|
||||
|
||||
template<>
|
||||
@@ -63,7 +59,7 @@ error_code cellSyncMutexInitialize(vm::ptr<CellSyncMutex> mutex)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSyncMutexLock(vm::ptr<CellSyncMutex> mutex)
|
||||
error_code cellSyncMutexLock(ppu_thread& ppu, vm::ptr<CellSyncMutex> mutex)
|
||||
{
|
||||
cellSync.trace("cellSyncMutexLock(mutex=*0x%x)", mutex);
|
||||
|
||||
@@ -81,10 +77,10 @@ error_code cellSyncMutexLock(vm::ptr<CellSyncMutex> mutex)
|
||||
const auto order = mutex->ctrl.atomic_op(&CellSyncMutex::lock_begin);
|
||||
|
||||
// Wait until rel value is equal to old acq value
|
||||
vm::wait_op(mutex.addr(), 4, [&]
|
||||
while (mutex->ctrl.load().rel != order)
|
||||
{
|
||||
return mutex->ctrl.load().rel == order;
|
||||
});
|
||||
ppu.test_state();
|
||||
}
|
||||
|
||||
_mm_mfence();
|
||||
|
||||
@@ -129,8 +125,6 @@ error_code cellSyncMutexUnlock(vm::ptr<CellSyncMutex> mutex)
|
||||
|
||||
mutex->ctrl.atomic_op(&CellSyncMutex::unlock);
|
||||
|
||||
vm::notify_at(mutex.addr(), 4);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -159,7 +153,7 @@ error_code cellSyncBarrierInitialize(vm::ptr<CellSyncBarrier> barrier, u16 total
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSyncBarrierNotify(vm::ptr<CellSyncBarrier> barrier)
|
||||
error_code cellSyncBarrierNotify(ppu_thread& ppu, vm::ptr<CellSyncBarrier> barrier)
|
||||
{
|
||||
cellSync.trace("cellSyncBarrierNotify(barrier=*0x%x)", barrier);
|
||||
|
||||
@@ -173,9 +167,10 @@ error_code cellSyncBarrierNotify(vm::ptr<CellSyncBarrier> barrier)
|
||||
return CELL_SYNC_ERROR_ALIGN;
|
||||
}
|
||||
|
||||
vm::wait_op(barrier.addr(), 4, [&] { return barrier->ctrl.atomic_op(&CellSyncBarrier::try_notify); });
|
||||
|
||||
vm::notify_at(barrier.addr(), 4);
|
||||
while (!barrier->ctrl.atomic_op(&CellSyncBarrier::try_notify))
|
||||
{
|
||||
ppu.test_state();
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -201,12 +196,10 @@ error_code cellSyncBarrierTryNotify(vm::ptr<CellSyncBarrier> barrier)
|
||||
return not_an_error(CELL_SYNC_ERROR_BUSY);
|
||||
}
|
||||
|
||||
vm::notify_at(barrier.addr(), 4);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSyncBarrierWait(vm::ptr<CellSyncBarrier> barrier)
|
||||
error_code cellSyncBarrierWait(ppu_thread& ppu, vm::ptr<CellSyncBarrier> barrier)
|
||||
{
|
||||
cellSync.trace("cellSyncBarrierWait(barrier=*0x%x)", barrier);
|
||||
|
||||
@@ -222,9 +215,10 @@ error_code cellSyncBarrierWait(vm::ptr<CellSyncBarrier> barrier)
|
||||
|
||||
_mm_mfence();
|
||||
|
||||
vm::wait_op(barrier.addr(), 4, [&] { return barrier->ctrl.atomic_op(&CellSyncBarrier::try_wait); });
|
||||
|
||||
vm::notify_at(barrier.addr(), 4);
|
||||
while (!barrier->ctrl.atomic_op(&CellSyncBarrier::try_wait))
|
||||
{
|
||||
ppu.test_state();
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@@ -250,8 +244,6 @@ error_code cellSyncBarrierTryWait(vm::ptr<CellSyncBarrier> barrier)
|
||||
return not_an_error(CELL_SYNC_ERROR_BUSY);
|
||||
}
|
||||
|
||||
vm::notify_at(barrier.addr(), 4);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -284,7 +276,7 @@ error_code cellSyncRwmInitialize(vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer,
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSyncRwmRead(vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer)
|
||||
error_code cellSyncRwmRead(ppu_thread& ppu, vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer)
|
||||
{
|
||||
cellSync.trace("cellSyncRwmRead(rwm=*0x%x, buffer=*0x%x)", rwm, buffer);
|
||||
|
||||
@@ -299,7 +291,10 @@ error_code cellSyncRwmRead(vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer)
|
||||
}
|
||||
|
||||
// wait until `writers` is zero, increase `readers`
|
||||
vm::wait_op(rwm.addr(), 8, [&] { return rwm->ctrl.atomic_op(&CellSyncRwm::try_read_begin); });
|
||||
while (!rwm->ctrl.atomic_op(&CellSyncRwm::try_read_begin))
|
||||
{
|
||||
ppu.test_state();
|
||||
}
|
||||
|
||||
// copy data to buffer
|
||||
std::memcpy(buffer.get_ptr(), rwm->buffer.get_ptr(), rwm->size);
|
||||
@@ -310,8 +305,6 @@ error_code cellSyncRwmRead(vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer)
|
||||
return CELL_SYNC_ERROR_ABORT;
|
||||
}
|
||||
|
||||
vm::notify_at(rwm.addr(), 8);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -344,12 +337,10 @@ error_code cellSyncRwmTryRead(vm::ptr<CellSyncRwm> rwm, vm::ptr<void> buffer)
|
||||
return CELL_SYNC_ERROR_ABORT;
|
||||
}
|
||||
|
||||
vm::notify_at(rwm.addr(), 8);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSyncRwmWrite(vm::ptr<CellSyncRwm> rwm, vm::cptr<void> buffer)
|
||||
error_code cellSyncRwmWrite(ppu_thread& ppu, vm::ptr<CellSyncRwm> rwm, vm::cptr<void> buffer)
|
||||
{
|
||||
cellSync.trace("cellSyncRwmWrite(rwm=*0x%x, buffer=*0x%x)", rwm, buffer);
|
||||
|
||||
@@ -364,10 +355,16 @@ error_code cellSyncRwmWrite(vm::ptr<CellSyncRwm> rwm, vm::cptr<void> buffer)
|
||||
}
|
||||
|
||||
// wait until `writers` is zero, set to 1
|
||||
vm::wait_op(rwm.addr(), 8, [&] { return rwm->ctrl.atomic_op(&CellSyncRwm::try_write_begin); });
|
||||
while (!rwm->ctrl.atomic_op(&CellSyncRwm::try_write_begin))
|
||||
{
|
||||
ppu.test_state();
|
||||
}
|
||||
|
||||
// wait until `readers` is zero
|
||||
vm::wait_op(rwm.addr(), 8, [&] { return rwm->ctrl.load().readers == 0; });
|
||||
while (rwm->ctrl.load().readers != 0)
|
||||
{
|
||||
ppu.test_state();
|
||||
}
|
||||
|
||||
// copy data from buffer
|
||||
std::memcpy(rwm->buffer.get_ptr(), buffer.get_ptr(), rwm->size);
|
||||
@@ -375,8 +372,6 @@ error_code cellSyncRwmWrite(vm::ptr<CellSyncRwm> rwm, vm::cptr<void> buffer)
|
||||
// sync and clear `readers` and `writers`
|
||||
rwm->ctrl.exchange({ 0, 0 });
|
||||
|
||||
vm::notify_at(rwm.addr(), 8);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -406,8 +401,6 @@ error_code cellSyncRwmTryWrite(vm::ptr<CellSyncRwm> rwm, vm::cptr<void> buffer)
|
||||
// sync and clear `readers` and `writers`
|
||||
rwm->ctrl.exchange({ 0, 0 });
|
||||
|
||||
vm::notify_at(rwm.addr(), 8);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -446,7 +439,7 @@ error_code cellSyncQueueInitialize(vm::ptr<CellSyncQueue> queue, vm::ptr<u8> buf
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSyncQueuePush(vm::ptr<CellSyncQueue> queue, vm::cptr<void> buffer)
|
||||
error_code cellSyncQueuePush(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue, vm::cptr<void> buffer)
|
||||
{
|
||||
cellSync.trace("cellSyncQueuePush(queue=*0x%x, buffer=*0x%x)", queue, buffer);
|
||||
|
||||
@@ -464,15 +457,16 @@ error_code cellSyncQueuePush(vm::ptr<CellSyncQueue> queue, vm::cptr<void> buffer
|
||||
|
||||
u32 position;
|
||||
|
||||
vm::wait_op(queue.addr(), 8, [&] { return queue->ctrl.atomic_op(&CellSyncQueue::try_push_begin, depth, &position); });
|
||||
while (!queue->ctrl.atomic_op(&CellSyncQueue::try_push_begin, depth, &position))
|
||||
{
|
||||
ppu.test_state();
|
||||
}
|
||||
|
||||
// copy data from the buffer at the position
|
||||
std::memcpy(&queue->buffer[position * queue->size], buffer.get_ptr(), queue->size);
|
||||
|
||||
queue->ctrl.atomic_op(&CellSyncQueue::push_end);
|
||||
|
||||
vm::notify_at(queue.addr(), 8);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -504,12 +498,10 @@ error_code cellSyncQueueTryPush(vm::ptr<CellSyncQueue> queue, vm::cptr<void> buf
|
||||
|
||||
queue->ctrl.atomic_op(&CellSyncQueue::push_end);
|
||||
|
||||
vm::notify_at(queue.addr(), 8);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSyncQueuePop(vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
|
||||
error_code cellSyncQueuePop(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
|
||||
{
|
||||
cellSync.trace("cellSyncQueuePop(queue=*0x%x, buffer=*0x%x)", queue, buffer);
|
||||
|
||||
@@ -527,15 +519,16 @@ error_code cellSyncQueuePop(vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
|
||||
|
||||
u32 position;
|
||||
|
||||
vm::wait_op(queue.addr(), 8, [&] { return queue->ctrl.atomic_op(&CellSyncQueue::try_pop_begin, depth, &position); });
|
||||
while (!queue->ctrl.atomic_op(&CellSyncQueue::try_pop_begin, depth, &position))
|
||||
{
|
||||
ppu.test_state();
|
||||
}
|
||||
|
||||
// copy data at the position to the buffer
|
||||
std::memcpy(buffer.get_ptr(), &queue->buffer[position % depth * queue->size], queue->size);
|
||||
|
||||
queue->ctrl.atomic_op(&CellSyncQueue::pop_end);
|
||||
|
||||
vm::notify_at(queue.addr(), 8);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -567,12 +560,10 @@ error_code cellSyncQueueTryPop(vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffe
|
||||
|
||||
queue->ctrl.atomic_op(&CellSyncQueue::pop_end);
|
||||
|
||||
vm::notify_at(queue.addr(), 8);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellSyncQueuePeek(vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
|
||||
error_code cellSyncQueuePeek(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
|
||||
{
|
||||
cellSync.trace("cellSyncQueuePeek(queue=*0x%x, buffer=*0x%x)", queue, buffer);
|
||||
|
||||
@@ -590,15 +581,16 @@ error_code cellSyncQueuePeek(vm::ptr<CellSyncQueue> queue, vm::ptr<void> buffer)
|
||||
|
||||
u32 position;
|
||||
|
||||
vm::wait_op(queue.addr(), 8, [&] { return queue->ctrl.atomic_op(&CellSyncQueue::try_peek_begin, depth, &position); });
|
||||
while (!queue->ctrl.atomic_op(&CellSyncQueue::try_peek_begin, depth, &position))
|
||||
{
|
||||
ppu.test_state();
|
||||
}
|
||||
|
||||
// copy data at the position to the buffer
|
||||
std::memcpy(buffer.get_ptr(), &queue->buffer[position % depth * queue->size], queue->size);
|
||||
|
||||
queue->ctrl.atomic_op(&CellSyncQueue::pop_end);
|
||||
|
||||
vm::notify_at(queue.addr(), 8);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -630,8 +622,6 @@ error_code cellSyncQueueTryPeek(vm::ptr<CellSyncQueue> queue, vm::ptr<void> buff
|
||||
|
||||
queue->ctrl.atomic_op(&CellSyncQueue::pop_end);
|
||||
|
||||
vm::notify_at(queue.addr(), 8);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -654,7 +644,7 @@ error_code cellSyncQueueSize(vm::ptr<CellSyncQueue> queue)
|
||||
return not_an_error(queue->ctrl.load().count & 0xffffff);
|
||||
}
|
||||
|
||||
error_code cellSyncQueueClear(vm::ptr<CellSyncQueue> queue)
|
||||
error_code cellSyncQueueClear(ppu_thread& ppu, vm::ptr<CellSyncQueue> queue)
|
||||
{
|
||||
cellSync.trace("cellSyncQueueClear(queue=*0x%x)", queue);
|
||||
|
||||
@@ -670,13 +660,18 @@ error_code cellSyncQueueClear(vm::ptr<CellSyncQueue> queue)
|
||||
|
||||
const u32 depth = queue->check_depth();
|
||||
|
||||
vm::wait_op(queue.addr(), 8, [&] { return queue->ctrl.atomic_op(&CellSyncQueue::try_clear_begin_1); });
|
||||
vm::wait_op(queue.addr(), 8, [&] { return queue->ctrl.atomic_op(&CellSyncQueue::try_clear_begin_2); });
|
||||
while (!queue->ctrl.atomic_op(&CellSyncQueue::try_clear_begin_1))
|
||||
{
|
||||
ppu.test_state();
|
||||
}
|
||||
|
||||
while (!queue->ctrl.atomic_op(&CellSyncQueue::try_clear_begin_2))
|
||||
{
|
||||
ppu.test_state();
|
||||
}
|
||||
|
||||
queue->ctrl.exchange({ 0, 0 });
|
||||
|
||||
vm::notify_at(queue.addr(), 8);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -848,8 +843,6 @@ error_code _cellSyncLFQueueGetPushPointer(ppu_thread& ppu, vm::ptr<CellSyncLFQue
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
|
||||
const auto old = queue->push1.load(); _mm_lfence();
|
||||
auto push = old;
|
||||
|
||||
@@ -1091,8 +1084,6 @@ error_code _cellSyncLFQueuePushBody(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> qu
|
||||
|
||||
while (true)
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
|
||||
s32 res;
|
||||
|
||||
if (queue->m_direction != CELL_SYNC_QUEUE_ANY2ANY)
|
||||
@@ -1111,7 +1102,7 @@ error_code _cellSyncLFQueuePushBody(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> qu
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(1ms); // hack
|
||||
ppu.test_state();
|
||||
}
|
||||
|
||||
const s32 depth = queue->m_depth;
|
||||
@@ -1147,8 +1138,6 @@ error_code _cellSyncLFQueueGetPopPointer(ppu_thread& ppu, vm::ptr<CellSyncLFQueu
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
|
||||
const auto old = queue->pop1.load(); _mm_lfence();
|
||||
auto pop = old;
|
||||
|
||||
@@ -1390,8 +1379,6 @@ error_code _cellSyncLFQueuePopBody(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> que
|
||||
|
||||
while (true)
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
|
||||
s32 res;
|
||||
|
||||
if (queue->m_direction != CELL_SYNC_QUEUE_ANY2ANY)
|
||||
@@ -1410,7 +1397,7 @@ error_code _cellSyncLFQueuePopBody(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> que
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(1ms); // hack
|
||||
ppu.test_state();
|
||||
}
|
||||
|
||||
const s32 depth = queue->m_depth;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user