Compare commits
270 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ac30c27c3f | |||
| 105c3ad9af | |||
| 25fb362c8b | |||
| 54b3673488 | |||
| 3e8b3b5f7c | |||
| 3319192abc | |||
| 423f41d7d3 | |||
| c27283c0d8 | |||
| 58816f0f3a | |||
| b79742b222 | |||
| b8c8161d58 | |||
| 2621d6a3e0 | |||
| 742cee9108 | |||
| 9f07674dfa | |||
| a08a768f50 | |||
| cfcb5684a0 | |||
| 0f121bdf37 | |||
| 3231f0aad0 | |||
| fe0600866c | |||
| 604e42bde8 | |||
| 7a02d6e4ed | |||
| bcc79b38b8 | |||
| 26d36e28fa | |||
| ccdd55a607 | |||
| 06da2071b4 | |||
| 7f804cd8c8 | |||
| ffd9ab12c6 | |||
| 5928b78c45 | |||
| 85f80cfbd1 | |||
| e7271179f7 | |||
| b6fd878834 | |||
| 508cf11b88 | |||
| 25bdc076cd | |||
| 30d45356a3 | |||
| 585a66ba21 | |||
| a2f6c27221 | |||
| a892a990f7 | |||
| 3bfe17a14f | |||
| ddd6ebc58d | |||
| 1b5c0c68dd | |||
| 3e6f9faf71 | |||
| ad72168143 | |||
| 4fc8276f0f | |||
| aad035c4b5 | |||
| 560d6776af | |||
| c7a9a8e8f1 | |||
| c2cd0c994f | |||
| c729d6d65a | |||
| 5785ebb384 | |||
| b32ad80c23 | |||
| 2b79a3691f | |||
| cff39ec3a9 | |||
| c4bdc930ca | |||
| 14b9b12211 | |||
| d45d8eb4bc | |||
| c5e6665010 | |||
| 1b5a479bbd | |||
| fc92040d7f | |||
| b3073bbf1e | |||
| 63a107acbc | |||
| 07646c2124 | |||
| b45cea1434 | |||
| 3648a058d1 | |||
| 90479eaad6 | |||
| 1de2ceca9b | |||
| 8fc281e034 | |||
| 2093134420 | |||
| 818dd88390 | |||
| be4bb48476 | |||
| 1c8cb3b7d3 | |||
| 1ede0f4706 | |||
| 78703cfe47 | |||
| c72a5a5775 | |||
| d09dd29054 | |||
| ed711c0e59 | |||
| fa6114719d | |||
| c8b9cc9fe5 | |||
| 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
-2
@@ -55,6 +55,7 @@
|
||||
rpcs3/git-version.h
|
||||
|
||||
# Visual Studio Files
|
||||
*.ipch
|
||||
*.vspx
|
||||
*.psess
|
||||
*.VC.*
|
||||
@@ -85,5 +86,3 @@ CMakeCache.txt
|
||||
# cotire
|
||||
rpcs3/cotire/*
|
||||
rpcs3/rpcs3_*_cotire.cmake
|
||||
/.vs/rpcs3/v15/ipch/AutoPCH/VKGSRENDER-1a48cf59/VKGSRENDER-717b8989/*.ipch
|
||||
/.vs/rpcs3/v15/ipch/EMUCORE-4bdb26ae/*.ipch
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[submodule "wxWidgets"]
|
||||
path = wxWidgets
|
||||
url = https://github.com/Zangetsu38/wxWidgets
|
||||
url = https://github.com/wxWidgets/wxWidgets
|
||||
ignore = dirty
|
||||
[submodule "rpcs3-ffmpeg"]
|
||||
path = 3rdparty/ffmpeg
|
||||
@@ -12,7 +12,7 @@
|
||||
[submodule "llvm"]
|
||||
path = llvm
|
||||
url = https://github.com/llvm-mirror/llvm
|
||||
branch = release_39
|
||||
branch = release_40
|
||||
[submodule "rsx_program_decompiler"]
|
||||
path = rsx_program_decompiler
|
||||
url = https://github.com/Zangetsu38/rsx_program_decompiler
|
||||
|
||||
+49
-48
@@ -1,90 +1,91 @@
|
||||
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 llvm40;
|
||||
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
|
||||
- cmake ..
|
||||
|
||||
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-4.0
|
||||
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-4.0
|
||||
- llvm-4.0-dev
|
||||
- libedit-dev
|
||||
- g++-5
|
||||
- gcc-5
|
||||
- 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
|
||||
|
||||
Vendored
+117
-6
@@ -6,7 +6,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Copyright (c) 2013-2016 The Khronos Group Inc.
|
||||
** Copyright (c) 2013-2017 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
@@ -31,9 +31,7 @@ extern "C" {
|
||||
** This header is generated from the Khronos OpenGL / OpenGL ES XML
|
||||
** API Registry. The current version of the Registry, generator scripts
|
||||
** used to make the header, and the header can be found at
|
||||
** http://www.opengl.org/registry/
|
||||
**
|
||||
** Khronos $Revision: 33248 $ on $Date: 2016-10-24 01:22:03 -0400 (Mon, 24 Oct 2016) $
|
||||
** https://github.com/KhronosGroup/OpenGL-Registry
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
|
||||
@@ -53,7 +51,7 @@ extern "C" {
|
||||
#define GLAPI extern
|
||||
#endif
|
||||
|
||||
#define GL_GLEXT_VERSION 20161024
|
||||
#define GL_GLEXT_VERSION 20170305
|
||||
|
||||
/* Generated C header for:
|
||||
* API: gl
|
||||
@@ -3355,6 +3353,16 @@ GLAPI void APIENTRY glFramebufferTextureFaceARB (GLenum target, GLenum attachmen
|
||||
#define GL_ARB_get_texture_sub_image 1
|
||||
#endif /* GL_ARB_get_texture_sub_image */
|
||||
|
||||
#ifndef GL_ARB_gl_spirv
|
||||
#define GL_ARB_gl_spirv 1
|
||||
#define GL_SHADER_BINARY_FORMAT_SPIR_V_ARB 0x9551
|
||||
#define GL_SPIR_V_BINARY_ARB 0x9552
|
||||
typedef void (APIENTRYP PFNGLSPECIALIZESHADERARBPROC) (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glSpecializeShaderARB (GLuint shader, const GLchar *pEntryPoint, GLuint numSpecializationConstants, const GLuint *pConstantIndex, const GLuint *pConstantValue);
|
||||
#endif
|
||||
#endif /* GL_ARB_gl_spirv */
|
||||
|
||||
#ifndef GL_ARB_gpu_shader5
|
||||
#define GL_ARB_gpu_shader5 1
|
||||
#endif /* GL_ARB_gpu_shader5 */
|
||||
@@ -5374,6 +5382,24 @@ GLAPI void APIENTRY glBlendEquationSeparateIndexedAMD (GLuint buf, GLenum modeRG
|
||||
#endif
|
||||
#endif /* GL_AMD_draw_buffers_blend */
|
||||
|
||||
#ifndef GL_AMD_framebuffer_sample_positions
|
||||
#define GL_AMD_framebuffer_sample_positions 1
|
||||
#define GL_SUBSAMPLE_DISTANCE_AMD 0x883F
|
||||
#define GL_PIXELS_PER_SAMPLE_PATTERN_X_AMD 0x91AE
|
||||
#define GL_PIXELS_PER_SAMPLE_PATTERN_Y_AMD 0x91AF
|
||||
#define GL_ALL_PIXELS_AMD 0xFFFFFFFF
|
||||
typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLEPOSITIONSFVAMDPROC) (GLenum target, GLuint numsamples, GLuint pixelindex, const GLfloat *values);
|
||||
typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLEPOSITIONSFVAMDPROC) (GLuint framebuffer, GLuint numsamples, GLuint pixelindex, const GLfloat *values);
|
||||
typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERFVAMDPROC) (GLenum target, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat *values);
|
||||
typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERFVAMDPROC) (GLuint framebuffer, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat *values);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glFramebufferSamplePositionsfvAMD (GLenum target, GLuint numsamples, GLuint pixelindex, const GLfloat *values);
|
||||
GLAPI void APIENTRY glNamedFramebufferSamplePositionsfvAMD (GLuint framebuffer, GLuint numsamples, GLuint pixelindex, const GLfloat *values);
|
||||
GLAPI void APIENTRY glGetFramebufferParameterfvAMD (GLenum target, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat *values);
|
||||
GLAPI void APIENTRY glGetNamedFramebufferParameterfvAMD (GLuint framebuffer, GLenum pname, GLuint numsamples, GLuint pixelindex, GLsizei size, GLfloat *values);
|
||||
#endif
|
||||
#endif /* GL_AMD_framebuffer_sample_positions */
|
||||
|
||||
#ifndef GL_AMD_gcn_shader
|
||||
#define GL_AMD_gcn_shader 1
|
||||
#endif /* GL_AMD_gcn_shader */
|
||||
@@ -5594,7 +5620,6 @@ GLAPI void APIENTRY glGetPerfMonitorCounterDataAMD (GLuint monitor, GLenum pname
|
||||
|
||||
#ifndef GL_AMD_sample_positions
|
||||
#define GL_AMD_sample_positions 1
|
||||
#define GL_SUBSAMPLE_DISTANCE_AMD 0x883F
|
||||
typedef void (APIENTRYP PFNGLSETMULTISAMPLEFVAMDPROC) (GLenum pname, GLuint index, const GLfloat *val);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glSetMultisamplefvAMD (GLenum pname, GLuint index, const GLfloat *val);
|
||||
@@ -8969,6 +8994,10 @@ GLAPI void APIENTRY glResizeBuffersMESA (void);
|
||||
#endif
|
||||
#endif /* GL_MESA_resize_buffers */
|
||||
|
||||
#ifndef GL_MESA_shader_integer_functions
|
||||
#define GL_MESA_shader_integer_functions 1
|
||||
#endif /* GL_MESA_shader_integer_functions */
|
||||
|
||||
#ifndef GL_MESA_window_pos
|
||||
#define GL_MESA_window_pos 1
|
||||
typedef void (APIENTRYP PFNGLWINDOWPOS2DMESAPROC) (GLdouble x, GLdouble y);
|
||||
@@ -9030,6 +9059,10 @@ GLAPI void APIENTRY glWindowPos4svMESA (const GLshort *v);
|
||||
#define GL_YCBCR_MESA 0x8757
|
||||
#endif /* GL_MESA_ycbcr_texture */
|
||||
|
||||
#ifndef GL_NVX_blend_equation_advanced_multi_draw_buffers
|
||||
#define GL_NVX_blend_equation_advanced_multi_draw_buffers 1
|
||||
#endif /* GL_NVX_blend_equation_advanced_multi_draw_buffers */
|
||||
|
||||
#ifndef GL_NVX_conditional_render
|
||||
#define GL_NVX_conditional_render 1
|
||||
typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVXPROC) (GLuint id);
|
||||
@@ -9049,6 +9082,32 @@ GLAPI void APIENTRY glEndConditionalRenderNVX (void);
|
||||
#define GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX 0x904B
|
||||
#endif /* GL_NVX_gpu_memory_info */
|
||||
|
||||
#ifndef GL_NVX_linked_gpu_multicast
|
||||
#define GL_NVX_linked_gpu_multicast 1
|
||||
#define GL_LGPU_SEPARATE_STORAGE_BIT_NVX 0x0800
|
||||
#define GL_MAX_LGPU_GPUS_NVX 0x92BA
|
||||
typedef void (APIENTRYP PFNGLLGPUNAMEDBUFFERSUBDATANVXPROC) (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data);
|
||||
typedef void (APIENTRYP PFNGLLGPUCOPYIMAGESUBDATANVXPROC) (GLuint sourceGpu, GLbitfield destinationGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srxY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth);
|
||||
typedef void (APIENTRYP PFNGLLGPUINTERLOCKNVXPROC) (void);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glLGPUNamedBufferSubDataNVX (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data);
|
||||
GLAPI void APIENTRY glLGPUCopyImageSubDataNVX (GLuint sourceGpu, GLbitfield destinationGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srxY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth);
|
||||
GLAPI void APIENTRY glLGPUInterlockNVX (void);
|
||||
#endif
|
||||
#endif /* GL_NVX_linked_gpu_multicast */
|
||||
|
||||
#ifndef GL_NV_alpha_to_coverage_dither_control
|
||||
#define GL_NV_alpha_to_coverage_dither_control 1
|
||||
#define GL_ALPHA_TO_COVERAGE_DITHER_DEFAULT_NV 0x934D
|
||||
#define GL_ALPHA_TO_COVERAGE_DITHER_ENABLE_NV 0x934E
|
||||
#define GL_ALPHA_TO_COVERAGE_DITHER_DISABLE_NV 0x934F
|
||||
#define GL_ALPHA_TO_COVERAGE_DITHER_MODE_NV 0x92BF
|
||||
typedef void (APIENTRYP PFNGLALPHATOCOVERAGEDITHERCONTROLNVPROC) (GLenum mode);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glAlphaToCoverageDitherControlNV (GLenum mode);
|
||||
#endif
|
||||
#endif /* GL_NV_alpha_to_coverage_dither_control */
|
||||
|
||||
#ifndef GL_NV_bindless_multi_draw_indirect
|
||||
#define GL_NV_bindless_multi_draw_indirect 1
|
||||
typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount);
|
||||
@@ -9342,6 +9401,23 @@ GLAPI void APIENTRY glDrawTextureNV (GLuint texture, GLuint sampler, GLfloat x0,
|
||||
#endif
|
||||
#endif /* GL_NV_draw_texture */
|
||||
|
||||
#ifndef GL_NV_draw_vulkan_image
|
||||
#define GL_NV_draw_vulkan_image 1
|
||||
typedef void (APIENTRY *GLVULKANPROCNV)(void);
|
||||
typedef void (APIENTRYP PFNGLDRAWVKIMAGENVPROC) (GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1);
|
||||
typedef GLVULKANPROCNV (APIENTRYP PFNGLGETVKPROCADDRNVPROC) (const GLchar *name);
|
||||
typedef void (APIENTRYP PFNGLWAITVKSEMAPHORENVPROC) (GLuint64 vkSemaphore);
|
||||
typedef void (APIENTRYP PFNGLSIGNALVKSEMAPHORENVPROC) (GLuint64 vkSemaphore);
|
||||
typedef void (APIENTRYP PFNGLSIGNALVKFENCENVPROC) (GLuint64 vkFence);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glDrawVkImageNV (GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1);
|
||||
GLAPI GLVULKANPROCNV APIENTRY glGetVkProcAddrNV (const GLchar *name);
|
||||
GLAPI void APIENTRY glWaitVkSemaphoreNV (GLuint64 vkSemaphore);
|
||||
GLAPI void APIENTRY glSignalVkSemaphoreNV (GLuint64 vkSemaphore);
|
||||
GLAPI void APIENTRY glSignalVkFenceNV (GLuint64 vkFence);
|
||||
#endif
|
||||
#endif /* GL_NV_draw_vulkan_image */
|
||||
|
||||
#ifndef GL_NV_evaluators
|
||||
#define GL_NV_evaluators 1
|
||||
#define GL_EVAL_2D_NV 0x86C0
|
||||
@@ -9576,6 +9652,41 @@ GLAPI void APIENTRY glFramebufferTextureFaceEXT (GLenum target, GLenum attachmen
|
||||
#define GL_NV_geometry_shader_passthrough 1
|
||||
#endif /* GL_NV_geometry_shader_passthrough */
|
||||
|
||||
#ifndef GL_NV_gpu_multicast
|
||||
#define GL_NV_gpu_multicast 1
|
||||
#define GL_PER_GPU_STORAGE_BIT_NV 0x0800
|
||||
#define GL_MULTICAST_GPUS_NV 0x92BA
|
||||
#define GL_RENDER_GPU_MASK_NV 0x9558
|
||||
#define GL_PER_GPU_STORAGE_NV 0x9548
|
||||
#define GL_MULTICAST_PROGRAMMABLE_SAMPLE_LOCATION_NV 0x9549
|
||||
typedef void (APIENTRYP PFNGLRENDERGPUMASKNVPROC) (GLbitfield mask);
|
||||
typedef void (APIENTRYP PFNGLMULTICASTBUFFERSUBDATANVPROC) (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const GLvoid *data);
|
||||
typedef void (APIENTRYP PFNGLMULTICASTCOPYBUFFERSUBDATANVPROC) (GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
|
||||
typedef void (APIENTRYP PFNGLMULTICASTCOPYIMAGESUBDATANVPROC) (GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth);
|
||||
typedef void (APIENTRYP PFNGLMULTICASTBLITFRAMEBUFFERNVPROC) (GLuint srcGpu, GLuint dstGpu, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
||||
typedef void (APIENTRYP PFNGLMULTICASTFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLuint gpu, GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v);
|
||||
typedef void (APIENTRYP PFNGLMULTICASTBARRIERNVPROC) (void);
|
||||
typedef void (APIENTRYP PFNGLMULTICASTWAITSYNCNVPROC) (GLuint signalGpu, GLbitfield waitGpuMask);
|
||||
typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTIVNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLint *params);
|
||||
typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTUIVNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLuint *params);
|
||||
typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTI64VNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLint64 *params);
|
||||
typedef void (APIENTRYP PFNGLMULTICASTGETQUERYOBJECTUI64VNVPROC) (GLuint gpu, GLuint id, GLenum pname, GLuint64 *params);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glRenderGpuMaskNV (GLbitfield mask);
|
||||
GLAPI void APIENTRY glMulticastBufferSubDataNV (GLbitfield gpuMask, GLuint buffer, GLintptr offset, GLsizeiptr size, const GLvoid *data);
|
||||
GLAPI void APIENTRY glMulticastCopyBufferSubDataNV (GLuint readGpu, GLbitfield writeGpuMask, GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
|
||||
GLAPI void APIENTRY glMulticastCopyImageSubDataNV (GLuint srcGpu, GLbitfield dstGpuMask, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth);
|
||||
GLAPI void APIENTRY glMulticastBlitFramebufferNV (GLuint srcGpu, GLuint dstGpu, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
||||
GLAPI void APIENTRY glMulticastFramebufferSampleLocationsfvNV (GLuint gpu, GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v);
|
||||
GLAPI void APIENTRY glMulticastBarrierNV (void);
|
||||
GLAPI void APIENTRY glMulticastWaitSyncNV (GLuint signalGpu, GLbitfield waitGpuMask);
|
||||
GLAPI void APIENTRY glMulticastGetQueryObjectivNV (GLuint gpu, GLuint id, GLenum pname, GLint *params);
|
||||
GLAPI void APIENTRY glMulticastGetQueryObjectuivNV (GLuint gpu, GLuint id, GLenum pname, GLuint *params);
|
||||
GLAPI void APIENTRY glMulticastGetQueryObjecti64vNV (GLuint gpu, GLuint id, GLenum pname, GLint64 *params);
|
||||
GLAPI void APIENTRY glMulticastGetQueryObjectui64vNV (GLuint gpu, GLuint id, GLenum pname, GLuint64 *params);
|
||||
#endif
|
||||
#endif /* GL_NV_gpu_multicast */
|
||||
|
||||
#ifndef GL_NV_gpu_program4
|
||||
#define GL_NV_gpu_program4 1
|
||||
#define GL_MIN_PROGRAM_TEXEL_OFFSET_NV 0x8904
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/GSL updated: fc5fce4f4f...96eaf274f8
@@ -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
|
||||
|
||||
@@ -22,8 +21,8 @@ If you want to contribute please take a look at the [Coding Style](https://githu
|
||||
### Dependencies
|
||||
|
||||
__Windows__
|
||||
* [Visual Studio 2015](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs.aspx)
|
||||
* [Visual C++ Redistributable Packages for Visual Studio 2015](http://www.microsoft.com/en-us/download/details.aspx?id=48145)
|
||||
* [Visual Studio 2017](https://www.visualstudio.com/downloads/)
|
||||
* [Visual C++ Redistributable Packages for Visual Studio 2017](https://go.microsoft.com/fwlink/?LinkId=746572)
|
||||
* [Cmake 3.1.0+](http://www.cmake.org/download/) (required; add to PATH)
|
||||
* [Python 3.3+](https://www.python.org/downloads/) (required; add to PATH)
|
||||
|
||||
|
||||
+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>(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;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#define GSL_THROW_ON_CONTRACT_VIOLATION
|
||||
|
||||
#pragma push_macro("new")
|
||||
#undef new
|
||||
#include <gsl.h>
|
||||
#include <gsl>
|
||||
#pragma pop_macro("new")
|
||||
#undef Expects
|
||||
#undef Ensures
|
||||
|
||||
+186
-151
@@ -10,6 +10,7 @@
|
||||
#include "StrFmt.h"
|
||||
#include "File.h"
|
||||
#include "Log.h"
|
||||
#include "VirtualMemory.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
@@ -25,12 +26,6 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#include "JIT.h"
|
||||
@@ -44,40 +39,36 @@ static const u64 s_memory_size = 0x20000000;
|
||||
// Try to reserve a portion of virtual memory in the first 2 GB address space beforehand, if possible.
|
||||
static void* const s_memory = []() -> void*
|
||||
{
|
||||
#ifdef _WIN32
|
||||
for (u64 addr = 0x10000000; addr <= 0x60000000; addr += 0x1000000)
|
||||
for (u64 addr = 0x10000000; addr <= 0x80000000 - s_memory_size; addr += 0x1000000)
|
||||
{
|
||||
if (VirtualAlloc((void*)addr, s_memory_size, MEM_RESERVE, PAGE_NOACCESS))
|
||||
if (auto ptr = utils::memory_reserve(s_memory_size, (void*)addr))
|
||||
{
|
||||
return (void*)addr;
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
return VirtualAlloc(NULL, s_memory_size, MEM_RESERVE, PAGE_NOACCESS);
|
||||
#else
|
||||
return ::mmap((void*)0x10000000, s_memory_size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
#endif
|
||||
return utils::memory_reserve(s_memory_size);
|
||||
}();
|
||||
|
||||
// Code section
|
||||
static u8* s_code_addr;
|
||||
static u64 s_code_size;
|
||||
|
||||
// EH frames
|
||||
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))
|
||||
std::array<u8, 16>* m_tramps;
|
||||
|
||||
MemoryManager(std::unordered_map<std::string, std::uintptr_t>& table)
|
||||
: m_link(table)
|
||||
, m_next(s_memory)
|
||||
, m_tramps(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -88,23 +79,57 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
||||
|
||||
virtual u64 getSymbolAddress(const std::string& name) override
|
||||
{
|
||||
if (u64 addr = RTDyldMemoryManager::getSymbolAddress(name))
|
||||
auto& addr = m_link[name];
|
||||
|
||||
// Find function address
|
||||
if (!addr)
|
||||
{
|
||||
// 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;
|
||||
addr = RTDyldMemoryManager::getSymbolAddress(name);
|
||||
|
||||
if (addr)
|
||||
{
|
||||
LOG_WARNING(GENERAL, "LLVM: Symbol requested: %s -> 0x%016llx", name, addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's fine if some function is never called, for example.
|
||||
LOG_ERROR(GENERAL, "LLVM: Linkage failed: %s", name);
|
||||
addr = (u64)null;
|
||||
}
|
||||
}
|
||||
|
||||
const auto found = table.find(name);
|
||||
|
||||
if (found != table.end())
|
||||
// Verify address for small code model
|
||||
if ((u64)s_memory > 0x80000000 - s_memory_size ? (u64)addr - (u64)s_memory >= s_memory_size : addr >= 0x80000000)
|
||||
{
|
||||
return found->second;
|
||||
// Allocate memory for trampolines
|
||||
if (!m_tramps)
|
||||
{
|
||||
m_tramps = reinterpret_cast<decltype(m_tramps)>(m_next);
|
||||
utils::memory_commit(m_next, 4096, utils::protection::wx);
|
||||
m_next = (u8*)((u64)m_next + 4096);
|
||||
}
|
||||
|
||||
// Create a trampoline
|
||||
auto& data = *m_tramps++;
|
||||
data[0x0] = 0xff; // JMP [rip+2]
|
||||
data[0x1] = 0x25;
|
||||
data[0x2] = 0x02;
|
||||
data[0x3] = 0x00;
|
||||
data[0x4] = 0x00;
|
||||
data[0x5] = 0x00;
|
||||
data[0x6] = 0x48; // MOV rax, imm64 (not executed)
|
||||
data[0x7] = 0xb8;
|
||||
std::memcpy(data.data() + 8, &addr, 8);
|
||||
addr = (u64)&data;
|
||||
|
||||
// Reset pointer (memory page exhausted)
|
||||
if (((u64)m_tramps % 4096) == 0)
|
||||
{
|
||||
m_tramps = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// It's fine if some function is never called, for example.
|
||||
LOG_ERROR(GENERAL, "LLVM: Linkage failed for %s", name);
|
||||
return (u64)null;
|
||||
return addr;
|
||||
}
|
||||
|
||||
virtual u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
@@ -118,20 +143,11 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!VirtualAlloc(m_next, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
|
||||
#else
|
||||
if (::mprotect(m_next, size, PROT_READ | PROT_WRITE | PROT_EXEC))
|
||||
#endif
|
||||
{
|
||||
LOG_FATAL(GENERAL, "LLVM: Failed to allocate memory at %p", m_next);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
utils::memory_commit(m_next, size, utils::protection::wx);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -151,38 +167,49 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
||||
LOG_ERROR(GENERAL, "LLVM: Writeable data section not supported!");
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!VirtualAlloc(m_next, size, MEM_COMMIT, PAGE_READWRITE))
|
||||
#else
|
||||
if (::mprotect(m_next, size, PROT_READ | PROT_WRITE))
|
||||
#endif
|
||||
{
|
||||
LOG_FATAL(GENERAL, "LLVM: Failed to allocate memory at %p", m_next);
|
||||
return nullptr;
|
||||
}
|
||||
utils::memory_commit(m_next, size);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
virtual void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
{
|
||||
s_unwind_info = addr;
|
||||
s_unwind_size = size;
|
||||
#ifdef _WIN32
|
||||
// Use s_memory as a BASE, compute the difference
|
||||
const u64 code_diff = (u64)s_code_addr - (u64)s_memory;
|
||||
const u64 unwind_diff = (u64)addr - (u64)s_memory;
|
||||
|
||||
// Fix RUNTIME_FUNCTION records (.pdata section)
|
||||
auto& pdata = s_unwind.back();
|
||||
|
||||
for (auto& rf : pdata)
|
||||
{
|
||||
rf.BeginAddress += static_cast<DWORD>(code_diff);
|
||||
rf.EndAddress += static_cast<DWORD>(code_diff);
|
||||
rf.UnwindData += static_cast<DWORD>(unwind_diff);
|
||||
}
|
||||
|
||||
// Register .xdata UNWIND_INFO structs
|
||||
if (!RtlAddFunctionTable(pdata.data(), (DWORD)pdata.size(), (u64)s_memory))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "RtlAddFunctionTable() failed! Error %u", GetLastError());
|
||||
}
|
||||
#endif
|
||||
|
||||
return RTDyldMemoryManager::registerEHFrames(addr, load_addr, size);
|
||||
}
|
||||
@@ -197,62 +224,95 @@ 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());
|
||||
}
|
||||
}
|
||||
|
||||
if (!VirtualFree(s_memory, 0, MEM_DECOMMIT))
|
||||
{
|
||||
LOG_FATAL(GENERAL, "VirtualFree(%p) failed! Error %u", s_memory, GetLastError());
|
||||
}
|
||||
s_unwind.clear();
|
||||
#else
|
||||
if (::mprotect(s_memory, s_memory_size, PROT_NONE))
|
||||
{
|
||||
LOG_FATAL(GENERAL, "mprotect(%p) failed! Error %d", s_memory, errno);
|
||||
}
|
||||
|
||||
// TODO: unregister EH frames if necessary
|
||||
#endif
|
||||
|
||||
utils::memory_decommit(s_memory, s_memory_size);
|
||||
}
|
||||
|
||||
private:
|
||||
void* m_next = s_memory;
|
||||
void* m_next;
|
||||
};
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
for (auto it = obj.section_begin(), end = obj.section_end(); it != end; ++it)
|
||||
{
|
||||
llvm::StringRef name;
|
||||
it->getName(name);
|
||||
|
||||
if (name == ".pdata")
|
||||
{
|
||||
llvm::StringRef data;
|
||||
it->getContents(data);
|
||||
|
||||
std::vector<RUNTIME_FUNCTION> rfs(data.size() / sizeof(RUNTIME_FUNCTION));
|
||||
|
||||
auto offsets = reinterpret_cast<DWORD*>(rfs.data());
|
||||
|
||||
// Initialize .pdata section using relocation info
|
||||
for (auto ri = it->relocation_begin(), end = it->relocation_end(); ri != end; ++ri)
|
||||
{
|
||||
if (ri->getType() == 3 /*R_X86_64_GOT32*/)
|
||||
{
|
||||
const u64 value = *reinterpret_cast<const DWORD*>(data.data() + ri->getOffset());
|
||||
offsets[ri->getOffset() / sizeof(DWORD)] = static_cast<DWORD>(value + ri->getSymbol()->getAddress().get());
|
||||
}
|
||||
}
|
||||
|
||||
s_unwind.emplace_back(std::move(rfs));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
static EventListener s_listener;
|
||||
|
||||
jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unordered_map<std::string, std::uintptr_t>&& table)
|
||||
jit_compiler::jit_compiler(std::unordered_map<std::string, std::uintptr_t> init_linkage_info, std::string _cpu)
|
||||
: m_link(std::move(init_linkage_info))
|
||||
, m_cpu(std::move(_cpu))
|
||||
{
|
||||
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_engine.reset(llvm::EngineBuilder(std::move(_module))
|
||||
if (m_cpu.empty())
|
||||
{
|
||||
m_cpu = llvm::sys::getHostCPUName();
|
||||
}
|
||||
|
||||
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)
|
||||
.setCodeModel(llvm::CodeModel::Small)
|
||||
.setMCPU(m_cpu)
|
||||
.create());
|
||||
|
||||
if (!m_engine)
|
||||
@@ -260,10 +320,44 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
|
||||
fmt::throw_exception("LLVM: Failed to create ExecutionEngine: %s", result);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
@@ -277,65 +371,6 @@ jit_compiler::jit_compiler(std::unique_ptr<llvm::Module>&& _module, std::unorder
|
||||
// Delete IR to lower memory consumption
|
||||
func.deleteBody();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Register .xdata UNWIND_INFO (.pdata section is empty for some reason)
|
||||
std::set<u64> func_set;
|
||||
|
||||
for (const auto& pair : m_map)
|
||||
{
|
||||
func_set.emplace(pair.second);
|
||||
}
|
||||
|
||||
const u64 base = (u64)s_memory;
|
||||
const u8* bits = s_unwind_info;
|
||||
|
||||
s_unwind.clear();
|
||||
s_unwind.reserve(m_map.size());
|
||||
|
||||
for (const u64 addr : func_set)
|
||||
{
|
||||
// Find next function address
|
||||
const auto _next = func_set.upper_bound(addr);
|
||||
const u64 next = _next != func_set.end() ? *_next : (u64)s_code_addr + s_code_size;
|
||||
|
||||
// Generate RUNTIME_FUNCTION record
|
||||
RUNTIME_FUNCTION uw;
|
||||
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);
|
||||
|
||||
// Parse .xdata UNWIND_INFO record
|
||||
const u8 flags = *bits++; // Version and flags
|
||||
const u8 prolog = *bits++; // Size of prolog
|
||||
const u8 count = *bits++; // Count of unwind codes
|
||||
const u8 frame = *bits++; // Frame Reg + Off
|
||||
bits += ::align(std::max<u8>(1, count), 2) * sizeof(u16); // UNWIND_CODE array
|
||||
|
||||
if (flags != 1)
|
||||
{
|
||||
// Can't happen for trivial code
|
||||
LOG_ERROR(GENERAL, "LLVM: unsupported UNWIND_INFO version/flags (0x%02x)", flags);
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_TRACE(GENERAL, "LLVM: .xdata at 0x%llx: function 0x%x..0x%x: p0x%02x, c0x%02x, f0x%02x", uw.UnwindData + base, uw.BeginAddress + base, uw.EndAddress + base, prolog, count, frame);
|
||||
}
|
||||
|
||||
if (s_unwind_info + s_unwind_size != bits)
|
||||
{
|
||||
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))
|
||||
{
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
jit_compiler::~jit_compiler()
|
||||
|
||||
+21
-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,22 @@ 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;
|
||||
|
||||
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>, std::string _cpu);
|
||||
~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 +59,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;
|
||||
}
|
||||
|
||||
|
||||
+244
-836
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();
|
||||
}
|
||||
|
||||
+49
-11
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "Utilities/Log.h"
|
||||
#include "VirtualMemory.h"
|
||||
#ifdef _WIN32
|
||||
@@ -11,32 +11,70 @@
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
namespace memory_helper
|
||||
namespace utils
|
||||
{
|
||||
void* reserve_memory(size_t size)
|
||||
// Convert memory protection (internal)
|
||||
static auto operator +(protection prot)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return verify("reserve_memory" HERE, VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS));
|
||||
DWORD _prot = PAGE_NOACCESS;
|
||||
switch (prot)
|
||||
{
|
||||
case protection::rw: _prot = PAGE_READWRITE; break;
|
||||
case protection::ro: _prot = PAGE_READONLY; break;
|
||||
case protection::no: break;
|
||||
case protection::wx: _prot = PAGE_EXECUTE_READWRITE; break;
|
||||
case protection::rx: _prot = PAGE_EXECUTE_READ; break;
|
||||
}
|
||||
#else
|
||||
return verify("reserve_memory" HERE, ::mmap(nullptr, size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0));
|
||||
int _prot = PROT_NONE;
|
||||
switch (prot)
|
||||
{
|
||||
case protection::rw: _prot = PROT_READ | PROT_WRITE; break;
|
||||
case protection::ro: _prot = PROT_READ; break;
|
||||
case protection::no: break;
|
||||
case protection::wx: _prot = PROT_READ | PROT_WRITE | PROT_EXEC; break;
|
||||
case protection::rx: _prot = PROT_READ | PROT_EXEC; break;
|
||||
}
|
||||
#endif
|
||||
|
||||
return _prot;
|
||||
}
|
||||
|
||||
void* memory_reserve(std::size_t size, void* use_addr)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return verify("reserve_memory" HERE, ::VirtualAlloc(use_addr, size, MEM_RESERVE, PAGE_NOACCESS));
|
||||
#else
|
||||
return verify("reserve_memory" HERE, ::mmap(use_addr, size, PROT_NONE, MAP_ANON | MAP_PRIVATE | (use_addr ? MAP_FIXED : 0), -1, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
void commit_page_memory(void* pointer, size_t size)
|
||||
void memory_commit(void* pointer, std::size_t size, protection prot)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
verify(HERE), VirtualAlloc(pointer, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
verify(HERE), ::VirtualAlloc(pointer, size, MEM_COMMIT, +prot);
|
||||
#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) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void free_reserved_memory(void* pointer, size_t size)
|
||||
void memory_decommit(void* pointer, std::size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
verify(HERE), VirtualFree(pointer, 0, MEM_DECOMMIT);
|
||||
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
|
||||
}
|
||||
|
||||
void memory_protect(void* pointer, std::size_t size, protection prot)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD old;
|
||||
verify(HERE), ::VirtualProtect(pointer, size, +prot, &old);
|
||||
#else
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), +prot) != -1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
namespace memory_helper
|
||||
namespace utils
|
||||
{
|
||||
// Memory protection type
|
||||
enum class protection
|
||||
{
|
||||
rw, // Read + write (default)
|
||||
ro, // Read only
|
||||
no, // No access
|
||||
wx, // Read + write + execute
|
||||
rx, // Read + execute
|
||||
};
|
||||
|
||||
/**
|
||||
* Reserve `size` bytes of virtual memory and returns it.
|
||||
* The memory should be commited before usage.
|
||||
*/
|
||||
void* reserve_memory(std::size_t size);
|
||||
void* memory_reserve(std::size_t size, void* use_addr = nullptr);
|
||||
|
||||
/**
|
||||
* Commit `size` bytes of virtual memory starting at pointer.
|
||||
* That is, bake reserved memory with physical memory.
|
||||
* pointer should belong to a range of reserved memory.
|
||||
*/
|
||||
void commit_page_memory(void* pointer, std::size_t size);
|
||||
void memory_commit(void* pointer, std::size_t size, protection prot = protection::rw);
|
||||
|
||||
/**
|
||||
* Decommit all memory committed via commit_page_memory.
|
||||
*/
|
||||
void free_reserved_memory(void* pointer, std::size_t size);
|
||||
void memory_decommit(void* pointer, std::size_t size);
|
||||
|
||||
// Set memory protection
|
||||
void memory_protect(void* pointer, std::size_t size, protection prot);
|
||||
}
|
||||
|
||||
+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);
|
||||
|
||||
Submodule Vulkan/Vulkan-LoaderAndValidationLayers updated: 55974c1934...c89bef400b
+1
-1
Submodule Vulkan/glslang updated: 5d89d4d483...789086a820
+13
-9
@@ -1,27 +1,30 @@
|
||||
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/)
|
||||
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)
|
||||
- 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/lib/cmake/llvm -DZLIB_ROOT=C:/rpcs3/zlib/ -DVULKAN_PREBUILT=ON)
|
||||
|
||||
build_script:
|
||||
- cmake --build . --config Release -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||
|
||||
install:
|
||||
- appveyor DownloadFile "https://dl.dropboxusercontent.com/s/c1by0uulee6g8th/wxWidgets.7z?dl=0" -FileName wxWidgets.7z
|
||||
- appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B-98fOyaZKJ5YWVnb29JZXFQWkU" -FileName llvmlibs.7z
|
||||
- appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRY2k3Q2Yya05lcm8" -FileName llvmlibs.7z
|
||||
- appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRa21fbDQteTN1dGs" -FileName vulkan.7z
|
||||
- appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B6v_qtb9hkicQ2hHa2dRbF83cE0" -FileName zlib.7z
|
||||
- set WXWIN=C:\rpcs3\wxWidgets
|
||||
@@ -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
|
||||
|
||||
+1
-1
Submodule llvm updated: a093ef43dd...4423e35117
@@ -10,6 +10,6 @@ TEST_CLASS(rsx_common)
|
||||
std::vector<gsl::byte> dest_buffer(2200);
|
||||
std::vector<gsl::byte> src_buffer(100000); // Big enough
|
||||
|
||||
write_vertex_array_data_to_buffer(gsl::span<gsl::byte>(dest_buffer), src_buffer.data(), 0, 550, rsx::vertex_base_type::ub256, 4, 20, 4);
|
||||
write_vertex_array_data_to_buffer(gsl::multi_span<gsl::byte>(dest_buffer), src_buffer.data(), 0, 550, rsx::vertex_base_type::ub256, 4, 20, 4);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26014.0
|
||||
VisualStudioVersion = 15.0.26206.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rpcs3", "rpcs3\rpcs3.vcxproj", "{70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
|
||||
+14
-6
@@ -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)
|
||||
@@ -80,7 +88,7 @@ if(NOT MSVC)
|
||||
endif()
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(OpenAL REQUIRED)
|
||||
find_package(LLVM 3.8 CONFIG)
|
||||
find_package(LLVM 4.0 CONFIG)
|
||||
|
||||
include("${wxWidgets_USE_FILE}")
|
||||
|
||||
@@ -125,7 +133,7 @@ ${LLVM_INCLUDE_DIRS}
|
||||
"${RPCS3_SRC_DIR}/.."
|
||||
"${RPCS3_SRC_DIR}/../Utilities/yaml-cpp/include"
|
||||
"${RPCS3_SRC_DIR}/../asmjit/src/asmjit"
|
||||
"${RPCS3_SRC_DIR}/../3rdparty/GSL/include"
|
||||
"${RPCS3_SRC_DIR}/../3rdparty/GSL/gsl"
|
||||
"${RPCS3_SRC_DIR}/../rsx_program_decompiler/rsx_decompiler"
|
||||
"${RPCS3_SRC_DIR}/../rsx_program_decompiler/shader_code"
|
||||
"${RPCS3_SRC_DIR}/../Vulkan/Vulkan-LoaderAndValidationLayers/include"
|
||||
@@ -143,7 +151,7 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
if(NOT LLVM_FOUND)
|
||||
Message("LLVM not found! LLVM 3.8 is required. RPCS3 will be compiled without LLVM support.")
|
||||
Message("LLVM not found! LLVM 4.0 is required. RPCS3 will be compiled without LLVM support.")
|
||||
else()
|
||||
add_definitions(${LLVM_DEFINITIONS})
|
||||
add_definitions(-DLLVM_AVAILABLE)
|
||||
@@ -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;
|
||||
@@ -150,7 +159,7 @@ class KeyVault
|
||||
std::vector<SELF_KEY> sk_LDR_arr;
|
||||
std::vector<SELF_KEY> sk_UNK7_arr;
|
||||
std::vector<SELF_KEY> sk_NPDRM_arr;
|
||||
u8 klicensee_key[0x10];
|
||||
u8 klicensee_key[0x10] = {};
|
||||
|
||||
public:
|
||||
KeyVault();
|
||||
|
||||
+2
-3
@@ -120,7 +120,7 @@ int decode_word(unsigned char *ptr, int index, int *bit_flag, unsigned int *rang
|
||||
|
||||
int decompress(unsigned char *out, unsigned char *in, unsigned int size)
|
||||
{
|
||||
__int64 result;
|
||||
int result;
|
||||
|
||||
unsigned char *tmp = new unsigned char[0xCC8];
|
||||
|
||||
@@ -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;
|
||||
|
||||
+453
-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 LoadedNpdrmKeys_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
|
||||
|
||||
+330
-109
@@ -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()
|
||||
@@ -858,10 +1089,11 @@ bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size)
|
||||
memcpy(klicensee_key, key_v.GetKlicenseeKey(), 0x10);
|
||||
|
||||
// Use klicensee if available.
|
||||
if (klicensee_key != NULL)
|
||||
memcpy(npdrm_key, klicensee_key, 0x10);
|
||||
|
||||
if (ctrl->npdrm.license == 1) // Network license.
|
||||
if (memcmp(klicensee_key, std::array<u8, 0x10>{0}.data(), 0x10))
|
||||
{
|
||||
memcpy(npdrm_key, klicensee_key, 0x10);
|
||||
}
|
||||
else if (ctrl->npdrm.license == 1) // Network license.
|
||||
{
|
||||
LOG_ERROR(LOADER, "SELF: Can't decrypt network NPDRM!");
|
||||
return false;
|
||||
@@ -900,25 +1132,29 @@ bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SELFDecrypter::LoadMetadata()
|
||||
bool SELFDecrypter::LoadMetadata(u8* klic_key)
|
||||
{
|
||||
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);
|
||||
|
||||
// Set klic if given
|
||||
if (klic_key)
|
||||
key_v.SetKlicenseeKey(klic_key);
|
||||
|
||||
// Copy the necessary parameters.
|
||||
u8 metadata_key[0x20];
|
||||
u8 metadata_iv[0x10];
|
||||
@@ -929,16 +1165,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 +1189,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 +1225,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 +1245,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 +1274,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 +1300,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 +1350,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 +1373,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 +1393,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 +1424,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 +1444,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 +1477,7 @@ bool CheckDebugSelf(const std::string& self, const std::string& elf)
|
||||
e.write(buf, size);
|
||||
}
|
||||
|
||||
s = std::move(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1291,53 +1485,80 @@ 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, u8* klic_key)
|
||||
{
|
||||
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())
|
||||
if (!self_dec.LoadMetadata(klic_key))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
extern bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key)
|
||||
{
|
||||
if (!self)
|
||||
return false;
|
||||
|
||||
self.seek(0);
|
||||
|
||||
if (self.size() >= 4 && self.read<u32>() == "SCE\0"_u32)
|
||||
{
|
||||
// Check the ELF file class (32 or 64 bit).
|
||||
bool isElf32 = IsSelfElf32(self);
|
||||
|
||||
// Start the decrypter on this SELF file.
|
||||
SELFDecrypter self_dec(self);
|
||||
|
||||
// Load the SELF file headers.
|
||||
if (!self_dec.LoadHeaders(isElf32))
|
||||
{
|
||||
LOG_ERROR(LOADER, "SELF: Failed to load SELF file headers!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load and decrypt the SELF file metadata.
|
||||
if (!self_dec.LoadMetadata(klic_key))
|
||||
{
|
||||
LOG_ERROR(LOADER, "SELF: Failed to load SELF file metadata!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
+34
-8
@@ -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,16 +407,14 @@ 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();
|
||||
bool LoadMetadata(u8* klic_key);
|
||||
bool DecryptData();
|
||||
bool DecryptNPDRM(u8 *metadata, u32 metadata_size);
|
||||
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, u8* klic_key = nullptr);
|
||||
extern bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key = nullptr);
|
||||
|
||||
+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);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
//#include "cell0054.h"
|
||||
|
||||
logs::channel cell0054("cell0054", logs::level::notice);
|
||||
|
||||
DECLARE(ppu_module_manager::cell0054)("cell0054", []()
|
||||
{
|
||||
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
//#include "cell005C.h"
|
||||
|
||||
logs::channel cell005C("cell005C", logs::level::notice);
|
||||
|
||||
DECLARE(ppu_module_manager::cell005C)("cell005C", []()
|
||||
{
|
||||
|
||||
});
|
||||
@@ -1,7 +1,8 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "Emu/Cell/lv2/sys_sync.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@@ -89,6 +90,7 @@ public:
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CELL_ADEC_TYPE_AC3:
|
||||
case CELL_ADEC_TYPE_ATRACX:
|
||||
case CELL_ADEC_TYPE_ATRACX_2CH:
|
||||
case CELL_ADEC_TYPE_ATRACX_6CH:
|
||||
@@ -199,6 +201,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 +378,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;
|
||||
}
|
||||
|
||||
@@ -508,6 +513,7 @@ bool adecCheckType(s32 type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CELL_ADEC_TYPE_AC3: cellAdec.notice("adecCheckType(): AC3"); break;
|
||||
case CELL_ADEC_TYPE_ATRACX: cellAdec.notice("adecCheckType(): ATRAC3plus"); break;
|
||||
case CELL_ADEC_TYPE_ATRACX_2CH: cellAdec.notice("adecCheckType(): ATRAC3plus 2ch"); break;
|
||||
case CELL_ADEC_TYPE_ATRACX_6CH: cellAdec.notice("adecCheckType(): ATRAC3plus 6ch"); break;
|
||||
@@ -515,7 +521,6 @@ bool adecCheckType(s32 type)
|
||||
case CELL_ADEC_TYPE_MP3: cellAdec.notice("adecCheckType(): MP3"); break;
|
||||
|
||||
case CELL_ADEC_TYPE_LPCM_PAMF:
|
||||
case CELL_ADEC_TYPE_AC3:
|
||||
case CELL_ADEC_TYPE_ATRAC3:
|
||||
case CELL_ADEC_TYPE_MPEG_L2:
|
||||
case CELL_ADEC_TYPE_CELP:
|
||||
@@ -608,9 +613,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);
|
||||
@@ -632,6 +635,7 @@ s32 cellAdecStartSeq(u32 handle, u32 param)
|
||||
|
||||
switch (adec->type)
|
||||
{
|
||||
case CELL_ADEC_TYPE_AC3:
|
||||
case CELL_ADEC_TYPE_ATRACX:
|
||||
case CELL_ADEC_TYPE_ATRACX_2CH:
|
||||
case CELL_ADEC_TYPE_ATRACX_6CH:
|
||||
@@ -905,6 +909,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()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "cellAudioOut.h"
|
||||
@@ -128,16 +128,21 @@ s32 cellAudioOutConfigure(u32 audioOut, vm::ptr<CellAudioOutConfiguration> confi
|
||||
case CELL_AUDIO_OUT_PRIMARY:
|
||||
if (config->channel)
|
||||
{
|
||||
//Emu.GetAudioManager().GetInfo().mode.channel = config->channel;
|
||||
CELL_AUDIO_OUT_CHNUM_8;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
//Emu.GetAudioManager().GetInfo().mode.encoder = config->encoder;
|
||||
|
||||
if (config->downMixer)
|
||||
{
|
||||
//Emu.GetAudioManager().GetInfo().mode.downMixer = config->downMixer;
|
||||
CELL_AUDIO_OUT_DOWNMIXER_NONE;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
if (config->encoder)
|
||||
{
|
||||
CELL_AUDIO_OUT_CODING_TYPE_LPCM;
|
||||
return CELL_OK;
|
||||
}
|
||||
return CELL_OK;
|
||||
|
||||
case CELL_AUDIO_OUT_SECONDARY:
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "cellAudioIn.h"
|
||||
#include "cellAudioOut.h"
|
||||
#include "cellVideoOut.h"
|
||||
#include "cellSysutil.h"
|
||||
|
||||
logs::channel cellAvconfExt("cellAvconfExt", logs::level::notice);
|
||||
|
||||
@@ -12,32 +13,38 @@ vm::gvar<f32> g_gamma; // TODO
|
||||
|
||||
s32 cellAudioOutUnregisterDevice()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellAudioOutGetDeviceInfo2()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellVideoOutSetXVColor()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellVideoOutSetupDisplay()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellAudioInGetDeviceInfo()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellVideoOutConvertCursorColor()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellVideoOutGetGamma(u32 videoOut, vm::ptr<f32> gamma)
|
||||
@@ -56,12 +63,14 @@ s32 cellVideoOutGetGamma(u32 videoOut, vm::ptr<f32> gamma)
|
||||
|
||||
s32 cellAudioInGetAvailableDeviceInfo()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellAudioOutGetAvailableDeviceInfo()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellVideoOutSetGamma(u32 videoOut, f32 gamma)
|
||||
@@ -85,28 +94,32 @@ s32 cellVideoOutSetGamma(u32 videoOut, f32 gamma)
|
||||
|
||||
s32 cellAudioOutRegisterDevice()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellAudioOutSetDeviceMode()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellAudioInSetDeviceMode()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellAudioInRegisterDevice()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellAudioInUnregisterDevice()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
UNIMPLEMENTED_FUNC(cellAvconfExt);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellVideoOutGetScreenSize(u32 videoOut, vm::ptr<float> screenSize)
|
||||
@@ -133,11 +146,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);
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
//#include "cellF044.h"
|
||||
|
||||
logs::channel cellF044("cellF044", logs::level::notice);
|
||||
|
||||
DECLARE(ppu_module_manager::cellF044)("cellF044", []()
|
||||
{
|
||||
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
@@ -743,9 +743,15 @@ s32 cellFontGraphicsGetLineRGBA()
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
}
|
||||
|
||||
s32 cellFontControl()
|
||||
{
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -828,4 +834,5 @@ DECLARE(ppu_module_manager::cellFont)("cellFont", []()
|
||||
REG_FUNC(cellFont, cellFontGetRenderEffectSlant);
|
||||
REG_FUNC(cellFont, cellFontGetScalePoint);
|
||||
REG_FUNC(cellFont, cellFontGraphicsGetLineRGBA);
|
||||
REG_FUNC(cellFont, cellFontControl);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "cellFontFT.h"
|
||||
@@ -26,9 +26,208 @@ s32 cellFontFTGetInitializedRevisionFlags()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTCacheStream_CacheEnd()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTCacheStream_CacheInit()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTCacheStream_CalcCacheIndexSize()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTCacheStream_Init()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_FontFamilyName()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_FontStyleName()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_GetAscender()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_GetBoundingBoxHeight()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_GetBoundingBoxMaxX()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_GetBoundingBoxMaxY()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_GetBoundingBoxMinX()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_GetBoundingBoxMinY()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_GetBoundingBoxWidth()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_GetGlyphImage()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_GetGlyphMetrics()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_GetMaxHorizontalAdvance()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_GetRenderBufferSize()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_GetRenderScale()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_GetRenderScalePoint()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_SetCompositeCodes()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_SetRenderScalePixel()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTFaceH_SetRenderScalePoint()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTManager_CloseFace()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTManager_Done_FreeType()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTManager_Init_FreeType()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTManager_OpenFileFace()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTManager_OpenMemFace()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 FTManager_OpenStreamFace()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFontFT);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
DECLARE(ppu_module_manager::cellFontFT)("cellFontFT", []()
|
||||
{
|
||||
REG_FUNC(cellFontFT, cellFontInitLibraryFreeTypeWithRevision);
|
||||
REG_FUNC(cellFontFT, cellFontFTGetRevisionFlags);
|
||||
REG_FUNC(cellFontFT, cellFontFTGetInitializedRevisionFlags);
|
||||
|
||||
REG_FUNC(cellFontFT, FTCacheStream_CacheEnd);
|
||||
REG_FUNC(cellFontFT, FTCacheStream_CacheInit);
|
||||
REG_FUNC(cellFontFT, FTCacheStream_CalcCacheIndexSize);
|
||||
REG_FUNC(cellFontFT, FTCacheStream_Init);
|
||||
|
||||
REG_FUNC(cellFontFT, FTFaceH_FontFamilyName);
|
||||
REG_FUNC(cellFontFT, FTFaceH_FontStyleName);
|
||||
REG_FUNC(cellFontFT, FTFaceH_GetAscender);
|
||||
REG_FUNC(cellFontFT, FTFaceH_GetBoundingBoxHeight);
|
||||
REG_FUNC(cellFontFT, FTFaceH_GetBoundingBoxMaxX);
|
||||
REG_FUNC(cellFontFT, FTFaceH_GetBoundingBoxMaxY);
|
||||
REG_FUNC(cellFontFT, FTFaceH_GetBoundingBoxMinX);
|
||||
REG_FUNC(cellFontFT, FTFaceH_GetBoundingBoxMinY);
|
||||
REG_FUNC(cellFontFT, FTFaceH_GetBoundingBoxWidth);
|
||||
REG_FUNC(cellFontFT, FTFaceH_GetGlyphImage);
|
||||
REG_FUNC(cellFontFT, FTFaceH_GetGlyphMetrics);
|
||||
REG_FUNC(cellFontFT, FTFaceH_GetMaxHorizontalAdvance);
|
||||
REG_FUNC(cellFontFT, FTFaceH_GetRenderBufferSize);
|
||||
REG_FUNC(cellFontFT, FTFaceH_GetRenderScale);
|
||||
REG_FUNC(cellFontFT, FTFaceH_GetRenderScalePoint);
|
||||
REG_FUNC(cellFontFT, FTFaceH_SetCompositeCodes);
|
||||
REG_FUNC(cellFontFT, FTFaceH_SetRenderScalePixel);
|
||||
REG_FUNC(cellFontFT, FTFaceH_SetRenderScalePoint);
|
||||
|
||||
REG_FUNC(cellFontFT, FTManager_CloseFace);
|
||||
REG_FUNC(cellFontFT, FTManager_Done_FreeType);
|
||||
REG_FUNC(cellFontFT, FTManager_Init_FreeType);
|
||||
REG_FUNC(cellFontFT, FTManager_OpenFileFace);
|
||||
REG_FUNC(cellFontFT, FTManager_OpenMemFace);
|
||||
REG_FUNC(cellFontFT, FTManager_OpenStreamFace);
|
||||
});
|
||||
|
||||
+241
-172
@@ -1,15 +1,18 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#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)
|
||||
@@ -22,6 +25,12 @@ s32 cellFsOpen(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, vm::cptr<void> a
|
||||
return sys_fs_open(path, flags, fd, flags & CELL_FS_O_CREAT ? CELL_FS_S_IRUSR | CELL_FS_S_IWUSR : 0, arg, size);
|
||||
}
|
||||
|
||||
s32 cellFsOpen2()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsRead(u32 fd, vm::ptr<void> buf, u64 nbytes, vm::ptr<u64> nread)
|
||||
{
|
||||
cellFs.trace("cellFsRead(fd=0x%x, buf=0x%x, nbytes=0x%llx, nread=0x%x)", fd, buf, nbytes, nread);
|
||||
@@ -168,6 +177,12 @@ s32 cellFsFGetBlockSize(u32 fd, vm::ptr<u64> sector_size, vm::ptr<u64> block_siz
|
||||
return sys_fs_fget_block_size(fd, sector_size, block_size, vm::var<u64>{}, vm::var<u64>{});
|
||||
}
|
||||
|
||||
s32 cellFsFGetBlockSize2()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsGetBlockSize(vm::cptr<char> path, vm::ptr<u64> sector_size, vm::ptr<u64> block_size)
|
||||
{
|
||||
cellFs.warning("cellFsGetBlockSize(path=%s, sector_size=*0x%x, block_size=*0x%x) -> sys_fs_get_block_size()", path, sector_size, block_size);
|
||||
@@ -178,6 +193,12 @@ s32 cellFsGetBlockSize(vm::cptr<char> path, vm::ptr<u64> sector_size, vm::ptr<u6
|
||||
return sys_fs_get_block_size(path, sector_size, block_size, vm::var<u64>{});
|
||||
}
|
||||
|
||||
s32 cellFsGetBlockSize2()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsTruncate(vm::cptr<char> path, u64 size)
|
||||
{
|
||||
cellFs.warning("cellFsTruncate(path=%s, size=0x%llx) -> sys_fs_truncate()", path, size);
|
||||
@@ -206,6 +227,22 @@ s32 cellFsChmod(vm::cptr<char> path, s32 mode)
|
||||
return sys_fs_chmod(path, mode);
|
||||
}
|
||||
|
||||
s32 cellFsChown()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
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 +258,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 +382,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 +403,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 +419,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 +435,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 +451,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 +467,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 +483,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 +499,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 +515,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 +531,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 +547,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 +563,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 +575,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 +584,58 @@ 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;
|
||||
}
|
||||
|
||||
s32 cellFsSdataOpenWithVersion()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -710,7 +665,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 +685,7 @@ struct fs_aio_thread : ppu_thread
|
||||
}
|
||||
|
||||
func(*this, aio, error, xid, result);
|
||||
lv2_obj::sleep(*this);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -737,12 +693,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 +700,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 +728,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 +743,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 +754,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 +769,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 +794,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 +804,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);
|
||||
@@ -906,36 +867,124 @@ s32 cellFsUnregisterL10nCallbacks()
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
}
|
||||
|
||||
s32 cellFsAclRead()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsAclWrite()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsFcntl()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsFdatasync()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsLink()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsLsnGetCDA()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsLsnGetCDASize()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsLsnLock()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsLsnRead()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsLsnRead2()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsLsnUnlock()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsMappedAllocate()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsMappedFree()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellFsSymbolicLink()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellFs);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
DECLARE(ppu_module_manager::cellFs)("sys_fs", []()
|
||||
{
|
||||
REG_FUNC(sys_fs, cellFsOpen);
|
||||
REG_FUNC(sys_fs, cellFsOpen2);
|
||||
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, cellFsSdataOpenWithVersion);
|
||||
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, cellFsChown);
|
||||
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, cellFsFGetBlockSize2);
|
||||
REG_FUNC(sys_fs, cellFsAioInit);
|
||||
REG_FUNC(sys_fs, cellFsAioFinish);
|
||||
REG_FUNC(sys_fs, cellFsAioRead);
|
||||
REG_FUNC(sys_fs, cellFsAioWrite);
|
||||
REG_FUNC(sys_fs, cellFsAioCancel);
|
||||
REG_FUNC(sys_fs, cellFsGetBlockSize);
|
||||
REG_FUNC(sys_fs, cellFsGetBlockSize2);
|
||||
REG_FUNC(sys_fs, cellFsGetFreeSize);
|
||||
REG_FUNC(sys_fs, cellFsReadWithOffset);
|
||||
REG_FUNC(sys_fs, cellFsWriteWithOffset);
|
||||
@@ -962,9 +1011,29 @@ 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);
|
||||
REG_FUNC(sys_fs, cellFsUnregisterL10nCallbacks);
|
||||
|
||||
REG_FUNC(sys_fs, cellFsAclRead);
|
||||
REG_FUNC(sys_fs, cellFsAclWrite);
|
||||
|
||||
REG_FUNC(sys_fs, cellFsFcntl);
|
||||
REG_FUNC(sys_fs, cellFsFdatasync);
|
||||
|
||||
REG_FUNC(sys_fs, cellFsLink);
|
||||
|
||||
REG_FUNC(sys_fs, cellFsLsnGetCDA);
|
||||
REG_FUNC(sys_fs, cellFsLsnGetCDASize);
|
||||
REG_FUNC(sys_fs, cellFsLsnLock);
|
||||
REG_FUNC(sys_fs, cellFsLsnRead);
|
||||
REG_FUNC(sys_fs, cellFsLsnRead2);
|
||||
REG_FUNC(sys_fs, cellFsLsnUnlock);
|
||||
|
||||
REG_FUNC(sys_fs, cellFsMappedAllocate);
|
||||
REG_FUNC(sys_fs, cellFsMappedFree);
|
||||
|
||||
REG_FUNC(sys_fs, cellFsSymbolicLink);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
@@ -179,9 +179,11 @@ s32 cellHddGameCheck2()
|
||||
fmt::throw_exception("Unimplemented" HERE);
|
||||
}
|
||||
|
||||
s32 cellHddGameGetSizeKB()
|
||||
s32 cellHddGameGetSizeKB(vm::ptr<u32> size)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGame);
|
||||
cellGame.todo("cellHddGameGetSizeKB(size=*0x%x)", size);
|
||||
|
||||
*size = 0;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -199,11 +201,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;
|
||||
}
|
||||
|
||||
@@ -248,7 +248,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
|
||||
@@ -406,6 +406,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;
|
||||
@@ -415,21 +417,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");
|
||||
|
||||
@@ -449,23 +445,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
|
||||
@@ -602,6 +630,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";
|
||||
@@ -671,9 +700,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;
|
||||
}
|
||||
|
||||
@@ -737,8 +768,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;
|
||||
@@ -757,9 +787,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,7 @@
|
||||
|
||||
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);
|
||||
|
||||
const u32 tiled_pitches[] = {
|
||||
0x00000000, 0x00000200, 0x00000300, 0x00000400,
|
||||
@@ -222,13 +221,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)
|
||||
@@ -379,13 +378,7 @@ s32 _cellGcmInitBody(vm::pptr<CellGcmContextData> context, u32 cmdSize, u32 ioSi
|
||||
current_context.begin.set(g_defaultCommandBufferBegin + 4096); // 4 kb reserved at the beginning
|
||||
current_context.end.set(g_defaultCommandBufferBegin + 32 * 1024 - 4); // 4b at the end for jump
|
||||
current_context.current = current_context.begin;
|
||||
current_context.callback.set(gcm_info.context_addr + 0x40);
|
||||
|
||||
vm::write32(gcm_info.context_addr + 0x40, gcm_info.context_addr + 0x48);
|
||||
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));
|
||||
current_context.callback.set(ppu_function_manager::addr + 8 * FIND_FUNC(cellGcmCallback));
|
||||
|
||||
vm::_ref<CellGcmContextData>(gcm_info.context_addr) = current_context;
|
||||
context->set(gcm_info.context_addr);
|
||||
@@ -1134,9 +1127,21 @@ void cellGcmSetDefaultCommandBuffer()
|
||||
vm::write32(fxm::get<GSRender>()->ctxt_addr, gcm_info.context_addr);
|
||||
}
|
||||
|
||||
s32 cellGcmSetDefaultCommandBufferAndSegmentWordSize()
|
||||
s32 cellGcmSetDefaultCommandBufferAndSegmentWordSize(u32 bufferSize, u32 segmentSize)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellGcmSys);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1289,12 +1294,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()));
|
||||
|
||||
@@ -1308,16 +1313,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());
|
||||
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
|
||||
#include "cellHttp.h"
|
||||
|
||||
logs::channel cellHttp("cellHttp", logs::level::notice);
|
||||
|
||||
|
||||
s32 cellHttpAuthCacheFlush()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellHttp);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellHttpInit()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellHttp);
|
||||
@@ -65,12 +72,30 @@ s32 cellHttpSessionCookieFlush()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellHttpCookieExport()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellHttp);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellHttpCookieExportWithClientId()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellHttp);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellHttpCookieFlush()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellHttp);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellHttpCookieImport()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellHttp);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellHttpCookieImportWithClientId()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellHttp);
|
||||
@@ -257,6 +282,18 @@ s32 cellHttpClientPollConnections()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellHttpClientSetConnectionStateCallback()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellHttp);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellHttpClientSetConnectionWaitStatus()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellHttp);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellHttpClientSetRecvTimeout()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellHttp);
|
||||
@@ -601,6 +638,7 @@ s32 cellHttpClientSetSslIdDestroyCallback()
|
||||
|
||||
DECLARE(ppu_module_manager::cellHttp)("cellHttp", []()
|
||||
{
|
||||
REG_FUNC(cellHttp, cellHttpAuthCacheFlush);
|
||||
REG_FUNC(cellHttp, cellHttpInit);
|
||||
REG_FUNC(cellHttp, cellHttpEnd);
|
||||
REG_FUNC(cellHttp, cellHttpsInit);
|
||||
@@ -612,8 +650,13 @@ DECLARE(ppu_module_manager::cellHttp)("cellHttp", []()
|
||||
REG_FUNC(cellHttp, cellHttpEndCookie);
|
||||
REG_FUNC(cellHttp, cellHttpAddCookieWithClientId);
|
||||
REG_FUNC(cellHttp, cellHttpSessionCookieFlush);
|
||||
|
||||
REG_FUNC(cellHttp, cellHttpCookieExport);
|
||||
REG_FUNC(cellHttp, cellHttpCookieExportWithClientId);
|
||||
REG_FUNC(cellHttp, cellHttpCookieFlush);
|
||||
REG_FUNC(cellHttp, cellHttpCookieImport);
|
||||
REG_FUNC(cellHttp, cellHttpCookieImportWithClientId);
|
||||
|
||||
REG_FUNC(cellHttp, cellHttpClientSetCookieSendCallback);
|
||||
REG_FUNC(cellHttp, cellHttpClientSetCookieRecvCallback);
|
||||
|
||||
@@ -647,6 +690,9 @@ DECLARE(ppu_module_manager::cellHttp)("cellHttp", []()
|
||||
REG_FUNC(cellHttp, cellHttpClientCloseAllConnections);
|
||||
REG_FUNC(cellHttp, cellHttpClientCloseConnections);
|
||||
REG_FUNC(cellHttp, cellHttpClientPollConnections);
|
||||
|
||||
REG_FUNC(cellHttp, cellHttpClientSetConnectionStateCallback);
|
||||
REG_FUNC(cellHttp, cellHttpClientSetConnectionWaitStatus);
|
||||
REG_FUNC(cellHttp, cellHttpClientSetRecvTimeout);
|
||||
REG_FUNC(cellHttp, cellHttpClientGetRecvTimeout);
|
||||
REG_FUNC(cellHttp, cellHttpClientSetSendTimeout);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
namespace vm { using namespace ps3; }
|
||||
|
||||
typedef vm::ptr<void> CellImeJpHandle;
|
||||
|
||||
enum {
|
||||
CELL_IMEJP_BEFORE_INPUT = 0,
|
||||
CELL_IMEJP_BEFORE_CONVERT = 1,
|
||||
CELL_IMEJP_CONVERTING = 2,
|
||||
CELL_IMEJP_CANDIDATE_EMPTY = 3,
|
||||
CELL_IMEJP_POSTCONVERT_KANA = 4,
|
||||
CELL_IMEJP_POSTCONVERT_HALF = 5,
|
||||
CELL_IMEJP_POSTCONVERT_RAW = 6,
|
||||
CELL_IMEJP_CANDIDATES = 7,
|
||||
CELL_IMEJP_MOVE_CLAUSE_GAP = 8,
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
#include "cellImejp.h"
|
||||
|
||||
logs::channel cellImeJp("cellImeJp", logs::level::notice);
|
||||
|
||||
@@ -15,21 +16,26 @@ enum
|
||||
CELL_IMEJP_ERROR_OTHER = 0x8002bfff,
|
||||
};
|
||||
|
||||
static uint16_t s_ime_string[256];
|
||||
|
||||
s32 cellImeJpOpen()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellImeJp);
|
||||
std::memset(s_ime_string, 0, sizeof(s_ime_string));
|
||||
cellImeJp.error("cellImeJpOpen()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellImeJpOpen2()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellImeJp);
|
||||
std::memset(s_ime_string, 0, sizeof(s_ime_string));
|
||||
cellImeJp.error("cellImeJpOpen2()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellImeJpOpen3()
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellImeJp);
|
||||
std::memset(s_ime_string, 0, sizeof(s_ime_string));
|
||||
cellImeJp.error("cellImeJpOpen3()");
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -69,9 +75,10 @@ s32 cellImeJpReset()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellImeJpGetStatus()
|
||||
s32 cellImeJpGetStatus(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pInputStatus)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellImeJp);
|
||||
cellImeJp.error("cellImeJpGetStatus()");
|
||||
*pInputStatus = CELL_IMEJP_CANDIDATE_EMPTY;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@@ -201,33 +208,42 @@ s32 cellImeJpGetFocusTop()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellImeJpGetFocusLength()
|
||||
s32 cellImeJpGetFocusLength(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pFocusLength)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellImeJp);
|
||||
cellImeJp.error("cellImeJpGetFocusLength()");
|
||||
*pFocusLength = 1;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellImeJpGetConfirmYomiString()
|
||||
s32 cellImeJpGetConfirmYomiString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pYomiString)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellImeJp);
|
||||
cellImeJp.error("cellImeJpGetConfirmYomiString()");
|
||||
*s_ime_string = 'A';
|
||||
*pYomiString = *s_ime_string;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellImeJpGetConfirmString()
|
||||
s32 cellImeJpGetConfirmString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pConfirmString)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellImeJp);
|
||||
cellImeJp.error("cellImeJpGetConfirmString()");
|
||||
*s_ime_string = 'A';
|
||||
*pConfirmString = *s_ime_string;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellImeJpGetConvertYomiString()
|
||||
s32 cellImeJpGetConvertYomiString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pYomiString)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellImeJp);
|
||||
cellImeJp.error("cellImeJpGetConvertYomiString()");
|
||||
*s_ime_string = 'A';
|
||||
*pYomiString = *s_ime_string;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
s32 cellImeJpGetConvertString()
|
||||
s32 cellImeJpGetConvertString(CellImeJpHandle hImeJpHandle, vm::ptr<u16> pConvertString)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellImeJp);
|
||||
cellImeJp.error("cellImeJpGetConvertString()");
|
||||
*s_ime_string = 'A';
|
||||
*pConvertString = *s_ime_string;
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user