Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e8238b8455 | |||
| cbf35ffdb5 | |||
| f90ddeabd0 | |||
| 372b9f9dc2 | |||
| a58a63aa72 | |||
| 4a220aa518 | |||
| c3a7eac375 | |||
| 82c31a9f73 | |||
| c530e3cf3d | |||
| 8e6fe717ab | |||
| d9350149e2 | |||
| 918a277e62 | |||
| 7a01b2ccb1 | |||
| b0742e08d9 | |||
| 9d635139ed | |||
| 788469a66e | |||
| f2df1da3a1 | |||
| a4fec2de6f | |||
| 56a0a3a608 | |||
| 6060c6a342 | |||
| 29453f6d16 | |||
| 65642c28c6 | |||
| 0ada09bfd3 | |||
| d49ee7886c | |||
| f4f4f16cdc | |||
| c1830965f5 | |||
| 4f14116a04 | |||
| d85dee5be4 | |||
| c2d9a0e512 | |||
| db9b2055ce | |||
| acfae4a34e | |||
| e80afa2b77 | |||
| 357decdfdc | |||
| c958715a19 | |||
| 9aba3e21fd | |||
| 52664cc501 | |||
| a13c875e75 | |||
| c47fb72015 |
+5
-3
@@ -11,11 +11,11 @@ BreakConstructorInitializersBeforeComma: true
|
||||
BreakBeforeBinaryOperators: false
|
||||
BreakBeforeTernaryOperators: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
Cpp11BracedListStyle: true
|
||||
IndentCaseLabels: false
|
||||
SortIncludes: false
|
||||
@@ -29,3 +29,5 @@ BinPackParameters: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
IndentWrappedFunctionNames: false
|
||||
|
||||
#SpaceAfterTemplateKeyword: false
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
root = true
|
||||
|
||||
[*.{h,cpp,hpp}]
|
||||
charset = utf-8-bom
|
||||
charset = utf-8
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
patreon: Nekotekina
|
||||
custom: https://rpcs3.net/alipay
|
||||
@@ -1,24 +0,0 @@
|
||||
<!---
|
||||
|
||||
THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO:
|
||||
https://forums.rpcs3.net
|
||||
or our discord:
|
||||
https://discord.me/RPCS3
|
||||
|
||||
PLEASE READ THE GUIDELINES BEFORE OPENING AN ISSUE:
|
||||
https://github.com/RPCS3/rpcs3/blob/master/.github/CONTRIBUTING.md
|
||||
|
||||
====================================================
|
||||
|
||||
When submitting an issue, please check the following:
|
||||
|
||||
- You have read the above.
|
||||
- You have provided the version (commit hash) of RPCS3 you are using.
|
||||
- You have provided sufficient detail for the issue to be reproduced.
|
||||
- You have provided system specs.
|
||||
- Please also provide:
|
||||
- For crashes, a backtrace.
|
||||
- For graphical issues, comparison screenshots with real hardware.
|
||||
|
||||
Remember that the GitHub Issue Tracker is not the place to ask for support or to submit Game Compatibility reports. You must use our forums for that.
|
||||
--->
|
||||
+26
-30
@@ -31,6 +31,7 @@
|
||||
*.wav
|
||||
|
||||
/build
|
||||
/bin
|
||||
/lib
|
||||
/tmp
|
||||
/ipch
|
||||
@@ -40,31 +41,42 @@
|
||||
/Vulkan/Vulkan-build
|
||||
/Vulkan/glslang-build
|
||||
|
||||
!/bin
|
||||
/bin/*
|
||||
|
||||
# Themes
|
||||
!/bin/GuiConfigs/
|
||||
/bin/GuiConfigs/*.ini
|
||||
/bin/GuiConfigs/*.ini.*
|
||||
/bin/GuiConfigs/*.dat
|
||||
/bin/GuiConfigs/*.dat.*
|
||||
/bin/rpcs3.ini
|
||||
/bin/rpcs3.ipdb
|
||||
/bin/rpcs3.iobj
|
||||
/bin/FragmentProgram.txt
|
||||
/bin/VertexProgram.txt
|
||||
/bin/*.hlsl
|
||||
/bin/BreakPoints.dat
|
||||
/bin/textures
|
||||
/bin/*.lib
|
||||
/bin/*.exp
|
||||
rpcs3/git-version.h
|
||||
|
||||
# Visual Studio Files
|
||||
.vs/*
|
||||
.vscode/*
|
||||
*.ipch
|
||||
*.vspx
|
||||
*.psess
|
||||
*.VC.*
|
||||
*.vcxproj.user
|
||||
enc_temp_folder/*
|
||||
CMakeSettings.json
|
||||
|
||||
# Copyrighted files
|
||||
/bin/data/
|
||||
/bin/dev_flash/data/font
|
||||
/bin/dev_flash/sys
|
||||
/bin/dev_flash/vsh
|
||||
|
||||
# Ignore installed games except test homebrews
|
||||
!/bin/dev_hdd0/game/
|
||||
/bin/dev_hdd0/game/*
|
||||
!/bin/dev_hdd0/game/TEST12345/
|
||||
|
||||
# Ignore other system generated files
|
||||
bin/dev_hdd0/*.txt
|
||||
x64/*
|
||||
rpcs3/x64/*
|
||||
rpcs3/git-version.h
|
||||
rpcs3-tests/x64/*
|
||||
|
||||
# cmake
|
||||
Makefile
|
||||
@@ -76,24 +88,8 @@ CMakeCache.txt
|
||||
rpcs3/cotire/*
|
||||
rpcs3/rpcs3_*_cotire.cmake
|
||||
|
||||
# kdevelop
|
||||
*.kdev4
|
||||
.kdev4/*
|
||||
|
||||
# Qt
|
||||
moc_*.cpp
|
||||
qrc_*.cpp
|
||||
qrc_resources.cpp
|
||||
rpcs3_automoc.cpp
|
||||
ui_*.h
|
||||
rpcs3/rpcs3_autogen/*
|
||||
|
||||
# QtCreator
|
||||
CMakeLists.txt.user
|
||||
|
||||
# CLion
|
||||
/.idea/*
|
||||
/cmake-build-*/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
|
||||
+21
-30
@@ -1,54 +1,45 @@
|
||||
[submodule "rpcs3-ffmpeg"]
|
||||
path = 3rdparty/ffmpeg
|
||||
url = https://github.com/hrydgard/ppsspp-ffmpeg
|
||||
ignore = dirty
|
||||
[submodule "asmjit"]
|
||||
path = asmjit
|
||||
url = https://github.com/kobalicek/asmjit
|
||||
ignore = dirty
|
||||
[submodule "llvm"]
|
||||
path = llvm
|
||||
url = https://github.com/RPCS3/llvm
|
||||
branch = release_60
|
||||
ignore = dirty
|
||||
url = https://github.com/llvm-mirror/llvm
|
||||
[submodule "rsx_program_decompiler"]
|
||||
path = rsx_program_decompiler
|
||||
url = https://github.com/RPCS3/rsx_program_decompiler
|
||||
[submodule "GSL"]
|
||||
path = 3rdparty/GSL
|
||||
url = https://github.com/Microsoft/GSL.git
|
||||
[submodule "libpng"]
|
||||
path = 3rdparty/libpng
|
||||
url = https://github.com/Zangetsu38/libpng
|
||||
ignore = dirty
|
||||
[submodule "Vulkan/glslang"]
|
||||
path = Vulkan/glslang
|
||||
url = https://github.com/KhronosGroup/glslang.git
|
||||
ignore = dirty
|
||||
[submodule "Vulkan/Vulkan-LoaderAndValidationLayers"]
|
||||
path = Vulkan/Vulkan-LoaderAndValidationLayers
|
||||
url = https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers
|
||||
[submodule "Utilities/yaml-cpp"]
|
||||
path = Utilities/yaml-cpp
|
||||
url = https://github.com/RPCS3/yaml-cpp
|
||||
[submodule "3rdparty/pugixml"]
|
||||
path = 3rdparty/pugixml
|
||||
url = https://github.com/RPCS3/pugixml
|
||||
[submodule "3rdparty/cereal"]
|
||||
path = 3rdparty/cereal
|
||||
url = https://github.com/USCiLab/cereal.git
|
||||
ignore = dirty
|
||||
[submodule "rsx-debugger"]
|
||||
path = rsx-debugger
|
||||
url = https://github.com/RPCS3/rsx-debugger.git
|
||||
[submodule "3rdparty/zlib"]
|
||||
path = 3rdparty/zlib
|
||||
url = https://github.com/madler/zlib
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/hidapi"]
|
||||
path = 3rdparty/hidapi
|
||||
url = https://github.com/RPCS3/hidapi
|
||||
branch = master
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/pugixml"]
|
||||
path = 3rdparty/pugixml
|
||||
url = https://github.com/zeux/pugixml
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/xxHash"]
|
||||
path = 3rdparty/xxHash
|
||||
url = https://github.com/Cyan4973/xxHash
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/yaml-cpp"]
|
||||
path = 3rdparty/yaml-cpp
|
||||
url = https://github.com/jbeder/yaml-cpp.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/libpng"]
|
||||
path = 3rdparty/libpng
|
||||
url = https://github.com/glennrp/libpng.git
|
||||
ignore = dirty
|
||||
[submodule "3rdparty/libusb"]
|
||||
path = 3rdparty/libusb
|
||||
url = https://github.com/RPCS3/libusb.git
|
||||
ignore = dirty
|
||||
url = https://github.com/Zangetsu38/hidapi
|
||||
branch = master
|
||||
+134
-30
@@ -1,35 +1,139 @@
|
||||
language: cpp
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
os:
|
||||
- linux
|
||||
#- osx
|
||||
|
||||
# osx_image: xcode6.4
|
||||
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
|
||||
cache:
|
||||
ccache: true
|
||||
directories:
|
||||
- $HOME/hombebrew_cache
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: BXyNziNYFLqLtY6Q58BDEUpRJr81HUtHpxf2Sr646bDcbG0Rf2rb2utqHZJ1Om926WdH7RqHuoVti4dr2X1lh4nHMkLD7MrIrfIkNN5T9aZdk19uw9IyMtz/cXon7/wqVo24tVqtBI0UghFxKii/GTDJEvRKgfpPp1e1vKSNGa4=
|
||||
# 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:
|
||||
include:
|
||||
# - os: linux
|
||||
# env:
|
||||
# - NAME="Linux build"
|
||||
# - COMPILER="gcc"
|
||||
# services: docker
|
||||
# cache: ccache
|
||||
# install: "docker pull rpcs3/rpcs3-travis-trusty:1.1"
|
||||
# script: 'travis_wait docker run -v $(pwd):/rpcs3 -v "$HOME/.ccache":/root/.ccache --env-file .travis/travis.env rpcs3/rpcs3-travis-trusty:1.1 /bin/bash -ex /rpcs3/.travis/build-linux.bash'
|
||||
- os: linux
|
||||
env:
|
||||
- NAME="Linux build"
|
||||
- COMPILER="clang"
|
||||
- DEPLOY_APPIMAGE="true"
|
||||
services: docker
|
||||
cache: ccache
|
||||
install: "docker pull rpcs3/rpcs3-travis-trusty:1.1"
|
||||
script: 'travis_wait docker run -v $(pwd):/rpcs3 -v "$HOME/.ccache":/root/.ccache --env-file .travis/travis.env rpcs3/rpcs3-travis-trusty:1.1 /bin/bash -ex /rpcs3/.travis/build-linux.bash'
|
||||
- os: osx
|
||||
osx_image: xcode10.2
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- ccache
|
||||
- glew
|
||||
- ninja
|
||||
- qt
|
||||
script: "/bin/bash -ex .travis/build-mac.bash"
|
||||
cache: ccache
|
||||
exclude:
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
include:
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: WITHOUT_LLVM="yes"
|
||||
|
||||
|
||||
git:
|
||||
depth: false # Unshallow clone to obtain proper GIT_VERSION
|
||||
submodules: false
|
||||
|
||||
before_install:
|
||||
- if [ "$CC" = "clang" ]; then
|
||||
export CXX="clang++-4.0" CC="clang-4.0";
|
||||
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;
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
sudo add-apt-repository ppa:beineri/opt-qt59-trusty -y;
|
||||
sudo apt-get update;
|
||||
sudo apt-get install qt59base -y --allow-unauthenticated;
|
||||
fi;
|
||||
# Add coverall for C++ so coverall.io could be triggered. Even it should be --coverage and gcov.
|
||||
# Install updated libglew-dev since the version provided by trusty is outdated
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
pip install --user cpp-coveralls requests[security];
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/main/g/glew/libglew-dev_1.13.0-2_amd64.deb;
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/main/g/glew/libglew1.13_1.13.0-2_amd64.deb;
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan-dev_1.0.21.0+dfsg1-1~16.04.1_amd64.deb;
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan1_1.0.21.0+dfsg1-1~16.04.1_amd64.deb;
|
||||
sudo dpkg -i libglew1.13_1.13.0-2_amd64.deb libglew-dev_1.13.0-2_amd64.deb libvulkan1_1.0.21.0+dfsg1-1~16.04.1_amd64.deb libvulkan-dev_1.0.21.0+dfsg1-1~16.04.1_amd64.deb;
|
||||
else
|
||||
brew update; brew update;
|
||||
brew install ccache glew llvm40;
|
||||
fi;
|
||||
|
||||
before_script:
|
||||
- git submodule update --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng Utilities/yaml-cpp 3rdparty/cereal 3rdparty/hidapi Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers
|
||||
- source /opt/qt59/bin/qt59-env.sh
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if [ -z "$WITHOUT_LLVM" ]; then
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr;
|
||||
else
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DWITHOUT_LLVM=ON;
|
||||
fi;
|
||||
- make -j 3
|
||||
- # AppImage generation
|
||||
- if [ "$TRAVIS_BRANCH" = "master" ] && [ "$CC" = "clang-4.0" ] && [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
||||
export LD_LIBRARY_PATH=${PWD}/3rdparty/libpng:/opt/qt59/lib/:$LD_LIBRARY_PATH ;
|
||||
make DESTDIR=appdir install ; find appdir/ ;
|
||||
find ../bin ;
|
||||
wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" ;
|
||||
chmod a+x linuxdeployqt*.AppImage ;
|
||||
unset QTDIR; unset QT_PLUGIN_PATH ;
|
||||
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs ;
|
||||
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -appimage ;
|
||||
find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq ;
|
||||
zip -r9 rpcs3.zip ./RPCS3*.AppImage;
|
||||
if [ -z "$WITHOUT_LLVM" ]; then
|
||||
export LLVM="-LLVM";
|
||||
fi;
|
||||
curl ${UPLOAD_URL}${TRAVIS_COMMIT:0:7}-${TRAVIS_BUILD_NUMBER}${LLVM}_linux64 --upload-file rpcs3.zip;
|
||||
fi;
|
||||
|
||||
script:
|
||||
# Add a command to show all the variables. May be useful for debugging Travis.
|
||||
#- echo "--Shell Export Lists START--" ; export -p; echo "--Shell Export Lists STOP--";
|
||||
# And to ensure the versions of toolchain
|
||||
- echo "--CXX version?"; "$CXX" --version; echo "--CXX version confirmed";
|
||||
#- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then make -j 3; fi
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-trusty-4.0
|
||||
packages:
|
||||
- cmake
|
||||
- libasound2-dev
|
||||
- libopenal-dev
|
||||
- freeglut3-dev
|
||||
#- libglew-dev
|
||||
#- libvulkan1
|
||||
#- libvulkan-dev
|
||||
- libc6-dev
|
||||
- llvm-4.0
|
||||
- llvm-4.0-dev
|
||||
- clang-4.0
|
||||
- libedit-dev
|
||||
- g++-5
|
||||
- gcc-5
|
||||
- libstdc++-5-dev
|
||||
- lib32stdc++6
|
||||
- zlib1g-dev
|
||||
# We need to install qt 5.8 manually because the version trusty provides is too old.
|
||||
#- qtbase5-dev
|
||||
- libudev-dev
|
||||
coverity_scan:
|
||||
project:
|
||||
name: $TRAVIS_REPO_SLUG
|
||||
description: "PS3 emulator/debugger"
|
||||
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
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/bin/env bash -ex
|
||||
shopt -s nocasematch
|
||||
|
||||
# Setup Qt variables
|
||||
export QT_BASE_DIR=/opt/qt${QTVERMIN}
|
||||
export PATH=$QT_BASE_DIR/bin:$PATH
|
||||
export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib
|
||||
|
||||
cd rpcs3
|
||||
|
||||
git submodule update --quiet --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng 3rdparty/cereal 3rdparty/hidapi 3rdparty/xxHash 3rdparty/yaml-cpp 3rdparty/libusb Vulkan/glslang
|
||||
|
||||
# Download pre-compiled llvm libs
|
||||
curl -sLO https://github.com/RPCS3/llvm/releases/download/continuous-linux-master/llvmlibs-linux.tar.gz
|
||||
mkdir llvmlibs
|
||||
tar -xzf ./llvmlibs-linux.tar.gz -C llvmlibs
|
||||
|
||||
mkdir build ; cd build
|
||||
|
||||
if [ $COMPILER = "gcc" ]; then
|
||||
# These are set in the dockerfile
|
||||
export CC=${GCC_BINARY}
|
||||
export CXX=${GXX_BINARY}
|
||||
else
|
||||
export CC=${CLANG_BINARY}
|
||||
export CXX=${CLANGXX_BINARY}
|
||||
fi
|
||||
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_LLVM_SUBMODULE=OFF -DLLVM_DIR=llvmlibs/lib/cmake/llvm/ -DUSE_NATIVE_INSTRUCTIONS=OFF -G Ninja
|
||||
|
||||
ninja; build_status=$?;
|
||||
|
||||
cd ..
|
||||
# If it compiled succesfully let's deploy
|
||||
if [ $build_status -eq 0 ] && [ -n "$GITHUB_TOKEN" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = false ]; then /bin/bash -ex .travis/deploy-linux.bash ; fi
|
||||
@@ -1,22 +0,0 @@
|
||||
export CCACHE_SLOPPINESS=pch_defines,time_macros
|
||||
export CMAKE_PREFIX_PATH=/usr/local/opt/qt5/
|
||||
export PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
|
||||
# Setup vulkan and gfx-rs/portability
|
||||
curl -sLO https://github.com/gfx-rs/portability/releases/download/latest/gfx-portability-macos-latest.zip
|
||||
unzip -: gfx-portability-macos-latest.zip
|
||||
curl -sLO https://github.com/KhronosGroup/Vulkan-Headers/archive/sdk-1.1.106.0.zip
|
||||
unzip -: sdk-*.zip
|
||||
mkdir vulkan-sdk
|
||||
ln -s ${PWD}/Vulkan-Headers*/include vulkan-sdk/include
|
||||
mkdir vulkan-sdk/lib
|
||||
cp target/release/libportability.dylib vulkan-sdk/lib/libVulkan.dylib
|
||||
# Let macdeployqt locate and install Vulkan library
|
||||
install_name_tool -id ${PWD}/vulkan-sdk/lib/libVulkan.dylib vulkan-sdk/lib/libVulkan.dylib
|
||||
export VULKAN_SDK=${PWD}/vulkan-sdk
|
||||
|
||||
git submodule update --quiet --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng 3rdparty/cereal 3rdparty/hidapi 3rdparty/libusb 3rdparty/xxHash 3rdparty/yaml-cpp Vulkan/glslang
|
||||
|
||||
mkdir build; cd build
|
||||
cmake .. -DWITH_LLVM=OFF -DUSE_NATIVE_INSTRUCTIONS=OFF -G Ninja
|
||||
ninja
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/bin/env bash -ex
|
||||
shopt -s nocasematch
|
||||
cd build
|
||||
if [ "$DEPLOY_APPIMAGE" = "true" ]; then
|
||||
DESTDIR=appdir ninja install
|
||||
curl -sLO "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage"
|
||||
chmod a+x linuxdeployqt*.AppImage
|
||||
./linuxdeployqt*.AppImage --appimage-extract
|
||||
./squashfs-root/AppRun ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs
|
||||
ls ./appdir/usr/lib/
|
||||
rm -r ./appdir/usr/share/doc
|
||||
rm ./appdir/usr/lib/libxcb*
|
||||
cp $(readlink -f /lib/x86_64-linux-gnu/libnsl.so.1) ./appdir/usr/lib/libnsl.so.1
|
||||
export PATH=/rpcs3/build/squashfs-root/usr/bin/:${PATH}
|
||||
|
||||
# Embed newer libstdc++ for distros that don't come with it (ubuntu 14.04, 16.04)
|
||||
mkdir -p appdir/usr/optional/ ; mkdir -p appdir/usr/optional/libstdc++/
|
||||
cp /usr/lib/x86_64-linux-gnu/libstdc++.so.6 ./appdir/usr/optional/libstdc++/
|
||||
rm ./appdir/AppRun
|
||||
curl -sL https://github.com/RPCS3/AppImageKit-checkrt/releases/download/continuous2/AppRun-patched-x86_64 -o ./appdir/AppRun
|
||||
chmod a+x ./appdir/AppRun
|
||||
curl -sL https://github.com/RPCS3/AppImageKit-checkrt/releases/download/continuous2/exec-x86_64.so -o ./appdir/usr/optional/exec.so
|
||||
|
||||
# Compile checker binary for AppImageKit-checkrt
|
||||
|
||||
# This may need updating if you update the compiler or rpcs3 uses newer c++ features
|
||||
# See https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/config/abi/pre/gnu.ver
|
||||
# for which definitions correlate to which CXXABI version.
|
||||
printf "#include <memory>\nint main(){std::make_exception_ptr(0);}" | $CXX -x c++ -o ./appdir/usr/optional/checker -
|
||||
|
||||
# Package it up and send it off
|
||||
./squashfs-root/usr/bin/appimagetool /rpcs3/build/appdir
|
||||
ls
|
||||
COMM_TAG="$(git describe --tags $(git rev-list --tags --max-count=1))"
|
||||
COMM_COUNT="$(git rev-list --count HEAD)"
|
||||
curl -sLO https://github.com/hcorion/uploadtool/raw/master/upload.sh
|
||||
|
||||
mv ./RPCS3*.AppImage rpcs3-${COMM_TAG}-${COMM_COUNT}-${TRAVIS_COMMIT:0:8}_linux64.AppImage
|
||||
|
||||
FILESIZEMB=$(bc <<< "scale=6; $(stat -c %s ./rpcs3*.AppImage)/1000000")
|
||||
SHA256SUM=($(sha256sum ./rpcs3*.AppImage))
|
||||
|
||||
unset TRAVIS_REPO_SLUG
|
||||
REPO_SLUG=RPCS3/rpcs3-binaries-linux \
|
||||
UPLOADTOOL_BODY="$SHA256SUM;${FILESIZEMB}MB"\
|
||||
RELEASE_NAME=build-${TRAVIS_COMMIT}\
|
||||
RELEASE_TITLE=${COMM_TAG}-${COMM_COUNT}\
|
||||
REPO_COMMIT=d812f1254a1157c80fd402f94446310560f54e5f\
|
||||
bash upload.sh rpcs3*.AppImage
|
||||
fi
|
||||
if [ "$DEPLOY_PPA" = "true" ]; then
|
||||
export DEBFULLNAME="RPCS3 Build Bot"
|
||||
fi
|
||||
@@ -1,9 +0,0 @@
|
||||
# Variables set by Travis CI
|
||||
TRAVIS_PULL_REQUEST
|
||||
TRAVIS_BRANCH
|
||||
TRAVIS_COMMIT
|
||||
# Variables for Travis build matrix
|
||||
COMPILER
|
||||
DEPLOY_APPIMAGE
|
||||
# Private Travis CI variables
|
||||
GITHUB_TOKEN
|
||||
Vendored
-378
@@ -1,378 +0,0 @@
|
||||
find_package(PkgConfig)
|
||||
include(ExternalProject)
|
||||
|
||||
if(APPLE)
|
||||
set(PLATFORM_ARCH "macosx/x86_64")
|
||||
elseif(WIN32)
|
||||
set(PLATFORM_ARCH "Windows/x86_64")
|
||||
else()
|
||||
set(PLATFORM_ARCH "linux/x86_64")
|
||||
endif()
|
||||
|
||||
# Dummy target to use when lib isn't available
|
||||
add_library(3rdparty_dummy_lib INTERFACE)
|
||||
|
||||
|
||||
# ZLib
|
||||
if (USE_SYSTEM_ZLIB)
|
||||
find_package(ZLIB QUIET)
|
||||
endif()
|
||||
|
||||
if (NOT ZLIB_FOUND)
|
||||
message(STATUS "Using builtin ZLIB")
|
||||
set(SKIP_INSTALL_ALL ON)
|
||||
add_subdirectory(zlib EXCLUDE_FROM_ALL)
|
||||
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/zlib" "${CMAKE_CURRENT_BINARY_DIR}/zlib")
|
||||
set(ZLIB_LIBRARY zlibstatic)
|
||||
endif()
|
||||
|
||||
add_library(3rdparty_zlib INTERFACE)
|
||||
target_link_libraries(3rdparty_zlib INTERFACE ${ZLIB_LIBRARY})
|
||||
target_include_directories(3rdparty_zlib INTERFACE ${ZLIB_INCLUDE_DIR})
|
||||
|
||||
# libPNG
|
||||
# Select the version of libpng to use, default is builtin
|
||||
if (NOT USE_SYSTEM_LIBPNG)
|
||||
# We use libpng's static library and don't need to build the shared library and run the tests
|
||||
set(PNG_SHARED OFF CACHE BOOL "Build shared lib" FORCE)
|
||||
set(PNG_TESTS OFF CACHE BOOL "Build libpng tests" FORCE)
|
||||
set(PNG_BUILD_ZLIB ON CACHE BOOL "ZLIB is already build or package is found" FORCE)
|
||||
set(SKIP_INSTALL_ALL ON)
|
||||
add_subdirectory(libpng EXCLUDE_FROM_ALL)
|
||||
|
||||
set(LIBPNG_TARGET png_static)
|
||||
|
||||
target_include_directories(png_static INTERFACE "${libpng_BINARY_DIR}" "${libpng_SOURCE_DIR}")
|
||||
else()
|
||||
find_package(PNG REQUIRED)
|
||||
|
||||
add_library(3rdparty_system_libpng INTERFACE)
|
||||
target_include_directories(3rdparty_system_libpng INTERFACE ${PNG_INCLUDE_DIR})
|
||||
target_link_libraries(3rdparty_system_libpng INTERFACE ${PNG_LIBRARY})
|
||||
target_compile_definitions(3rdparty_system_libpng INTERFACE ${PNG_DEFINITIONS})
|
||||
|
||||
set(LIBPNG_TARGET 3rdparty_system_libpng)
|
||||
endif()
|
||||
|
||||
|
||||
# pugixml
|
||||
add_subdirectory(pugixml EXCLUDE_FROM_ALL)
|
||||
|
||||
|
||||
# hidapi
|
||||
add_library(3rdparty_hidapi INTERFACE)
|
||||
target_include_directories(3rdparty_hidapi INTERFACE hidapi/hidapi)
|
||||
|
||||
if(APPLE)
|
||||
add_subdirectory(hidapi/mac EXCLUDE_FROM_ALL)
|
||||
target_include_directories(hidapi-mac PUBLIC hidapi/hidapi)
|
||||
|
||||
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-mac "-framework CoreFoundation" "-framework IOKit")
|
||||
elseif(CMAKE_SYSTEM MATCHES "Linux")
|
||||
add_subdirectory(hidapi/linux EXCLUDE_FROM_ALL)
|
||||
target_include_directories(hidapi-hidraw PUBLIC hidapi/hidapi)
|
||||
|
||||
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-hidraw udev)
|
||||
elseif(WIN32)
|
||||
add_subdirectory(hidapi/windows EXCLUDE_FROM_ALL)
|
||||
target_include_directories(hidapi-hid PUBLIC hidapi/hidapi)
|
||||
|
||||
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-hid Shlwapi.lib)
|
||||
else()
|
||||
add_subdirectory(hidapi/libusb EXCLUDE_FROM_ALL)
|
||||
target_include_directories(hidapi-libusb PUBLIC hidapi/hidapi)
|
||||
|
||||
target_link_libraries(3rdparty_hidapi INTERFACE hidapi-libusb usb)
|
||||
endif()
|
||||
|
||||
|
||||
# libusb
|
||||
if(CMAKE_SYSTEM MATCHES "DragonFly|FreeBSD")
|
||||
# Always use system libusb as reference implementation isn't supported
|
||||
add_library(usb-1.0-static INTERFACE)
|
||||
target_link_libraries(usb-1.0-static INTERFACE usb)
|
||||
elseif(MSVC)
|
||||
# Windows time.h defines timespec but doesn't add any flag for it, which makes libusb attempt to define it again
|
||||
add_definitions(-DHAVE_STRUCT_TIMESPEC=1)
|
||||
add_subdirectory(libusb EXCLUDE_FROM_ALL)
|
||||
else()
|
||||
add_subdirectory(libusb EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
|
||||
# yaml-cpp
|
||||
# We don't want to install yaml-cpp but its cmake file doesn't have option
|
||||
# to disable it...
|
||||
# So we just install it to a different directory
|
||||
set(CMAKE_INSTALL_PREFIX_OLD ${CMAKE_INSTALL_PREFIX})
|
||||
set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/yaml-cpp_install)
|
||||
|
||||
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "Enable testing" FORCE)
|
||||
set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "Enable parse tools" FORCE)
|
||||
set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "Enable contrib stuff in library" FORCE)
|
||||
add_subdirectory(yaml-cpp EXCLUDE_FROM_ALL)
|
||||
|
||||
set(CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX_OLD})
|
||||
|
||||
|
||||
# xxHash
|
||||
set(XXHASH_BUNDLED_MODE ON)
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Make xxHash build static libs")
|
||||
add_subdirectory(xxHash/cmake_unofficial EXCLUDE_FROM_ALL)
|
||||
target_include_directories(xxhash INTERFACE xxHash)
|
||||
|
||||
|
||||
# cereal
|
||||
add_library(3rdparty_cereal INTERFACE)
|
||||
target_include_directories(3rdparty_cereal INTERFACE cereal/include)
|
||||
|
||||
|
||||
# OpenGL
|
||||
|
||||
# Prefer GLVND for OpenGL rather than legacy
|
||||
set(OpenGL_GL_PREFERENCE GLVND)
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
add_library(3rdparty_opengl INTERFACE)
|
||||
target_include_directories(3rdparty_opengl INTERFACE GL)
|
||||
|
||||
if (WIN32)
|
||||
if(NOT MSVC)
|
||||
target_link_libraries(3rdparty_opengl INTERFACE ${OPENGL_LIBRARIES} opengl32.lib glu32.lib)
|
||||
else()
|
||||
target_link_libraries(3rdparty_opengl INTERFACE dxgi.lib d2d1.lib dwrite.lib)
|
||||
endif()
|
||||
else()
|
||||
target_link_libraries(3rdparty_opengl INTERFACE ${OPENGL_LIBRARIES})
|
||||
|
||||
target_compile_definitions(3rdparty_opengl
|
||||
INTERFACE
|
||||
-DGL_GLEXT_PROTOTYPES
|
||||
-DGLX_GLXEXT_PROTOTYPES)
|
||||
endif()
|
||||
|
||||
|
||||
# GSL
|
||||
add_library(3rdparty_gsl INTERFACE)
|
||||
target_include_directories(3rdparty_gsl INTERFACE GSL/include)
|
||||
|
||||
|
||||
# stblib
|
||||
add_library(3rdparty_stblib INTERFACE)
|
||||
target_include_directories(3rdparty_stblib INTERFACE stblib)
|
||||
|
||||
|
||||
# discord_rpc
|
||||
add_library(3rdparty_discord-rpc INTERFACE)
|
||||
|
||||
# We don't want Discord Rich Presence on the BSDs and other OSes
|
||||
if (USE_DISCORD_RPC AND (WIN32 OR CMAKE_SYSTEM MATCHES "Linux" OR APPLE))
|
||||
if (WIN32 AND NOT MSVC)
|
||||
ExternalProject_Add(discord-rpc
|
||||
GIT_REPOSITORY https://github.com/discordapp/discord-rpc
|
||||
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/discord-rpc
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
target_include_directories(3rdparty_discord-rpc INTERFACE discord-rpc/include)
|
||||
target_compile_definitions(3rdparty_discord-rpc INTERFACE -DWITH_DISCORD_RPC)
|
||||
|
||||
set(DISCORD_RPC_LIB NOTFOUND)
|
||||
if (WIN32)
|
||||
if (NOT MSVC)
|
||||
set(DISCORD_RPC_LIB ${CMAKE_CURRENT_BINARY_DIR}/discord-rpc/src/libdiscord-rpc.a)
|
||||
else()
|
||||
find_library(DISCORD_RPC_LIB discord-rpc PATHS discord-rpc/lib/ NO_DEFAULT_PATH)
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM MATCHES "Linux")
|
||||
find_library(DISCORD_RPC_LIB discord-rpc-linux PATHS discord-rpc/lib/ NO_DEFAULT_PATH)
|
||||
elseif(APPLE)
|
||||
find_library(DISCORD_RPC_LIB discord-rpc-mac PATHS discord-rpc/lib/ NO_DEFAULT_PATH)
|
||||
endif()
|
||||
target_link_libraries(3rdparty_discord-rpc INTERFACE ${DISCORD_RPC_LIB})
|
||||
if(APPLE)
|
||||
target_link_libraries(3rdparty_discord-rpc INTERFACE "objc" "-framework Foundation" "-framework CoreServices")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# ALSA
|
||||
set(ALSA_TARGET 3rdparty_dummy_lib)
|
||||
|
||||
if(USE_ALSA)
|
||||
find_package(ALSA)
|
||||
if(ALSA_FOUND)
|
||||
add_library(3rdparty_alsa INTERFACE)
|
||||
target_compile_definitions(3rdparty_alsa INTERFACE -DHAVE_ALSA)
|
||||
target_include_directories(3rdparty_alsa SYSTEM INTERFACE ${ALSA_INCLUDE_DIRS})
|
||||
target_link_libraries(3rdparty_alsa INTERFACE ${ALSA_LIBRARIES})
|
||||
|
||||
set(ALSA_TARGET 3rdparty_alsa)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Pulse
|
||||
set(PULSE_TARGET 3rdparty_dummy_lib)
|
||||
if(USE_PULSE)
|
||||
pkg_check_modules(PULSE libpulse-simple)
|
||||
|
||||
if(PULSE_FOUND)
|
||||
add_library(3rdparty_pulse INTERFACE)
|
||||
target_compile_definitions(3rdparty_pulse INTERFACE -DHAVE_PULSE)
|
||||
target_include_directories(3rdparty_pulse SYSTEM
|
||||
INTERFACE ${PULSE_INCLUDE_DIRS})
|
||||
target_link_libraries(3rdparty_pulse INTERFACE ${PULSE_LDFLAGS})
|
||||
|
||||
set(PULSE_TARGET 3rdparty_pulse)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# libevdev
|
||||
set(LIBEVDEV_TARGET 3rdparty_dummy_lib)
|
||||
if(USE_LIBEVDEV)
|
||||
pkg_check_modules(LIBEVDEV libevdev)
|
||||
if(LIBEVDEV_FOUND)
|
||||
add_library(3rdparty_libevdev INTERFACE)
|
||||
target_compile_definitions(3rdparty_libevdev INTERFACE -DHAVE_LIBEVDEV)
|
||||
target_include_directories(3rdparty_libevdev SYSTEM
|
||||
INTERFACE ${LIBEVDEV_INCLUDE_DIRS})
|
||||
target_link_libraries(3rdparty_libevdev INTERFACE ${LIBEVDEV_LDFLAGS})
|
||||
|
||||
set(LIBEVDEV_TARGET 3rdparty_libevdev)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Vulkan
|
||||
set(VULKAN_TARGET 3rdparty_dummy_lib)
|
||||
if(USE_VULKAN)
|
||||
find_package(Vulkan)
|
||||
if(VULKAN_FOUND)
|
||||
add_library(3rdparty_vulkan INTERFACE)
|
||||
target_compile_definitions(3rdparty_vulkan INTERFACE -DHAVE_VULKAN)
|
||||
target_link_libraries(3rdparty_vulkan INTERFACE SPIRV Vulkan::Vulkan)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(Wayland)
|
||||
if (WAYLAND_FOUND)
|
||||
target_include_directories(3rdparty_vulkan
|
||||
INTERFACE ${WAYLAND_INCLUDE_DIR})
|
||||
|
||||
target_compile_definitions(3rdparty_vulkan
|
||||
INTERFACE -DVK_USE_PLATFORM_WAYLAND_KHR)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(VULKAN_TARGET 3rdparty_vulkan)
|
||||
else()
|
||||
message("WARNING! USE_VULKAN was enabled, but libvulkan was not found. RPCS3 will be compiled without Vulkan support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# OpenAL
|
||||
if (MSVC)
|
||||
find_path(OPENAL_INCLUDE_DIR al.h PATHS OpenAL/include)
|
||||
find_library(OPENAL_LIBRARY OpenAL32 PATHS OpenAL/libs/Win64/)
|
||||
else()
|
||||
find_package(OpenAL REQUIRED)
|
||||
endif()
|
||||
|
||||
add_library(3rdparty_openal INTERFACE)
|
||||
target_include_directories(3rdparty_openal INTERFACE ${OPENAL_INCLUDE_DIR})
|
||||
target_link_libraries(3rdparty_openal INTERFACE ${OPENAL_LIBRARY})
|
||||
|
||||
|
||||
# FFMPEG
|
||||
add_library(3rdparty_ffmpeg INTERFACE)
|
||||
|
||||
# Select the version of ffmpeg to use, default is builtin
|
||||
if(USE_SYSTEM_FFMPEG)
|
||||
message("-- RPCS3: using shared ffmpeg")
|
||||
find_package(FFMPEG REQUIRED)
|
||||
|
||||
target_include_directories(3rdparty_ffmpeg INTERFACE ${FFMPEG_INCLUDE_DIR})
|
||||
target_link_libraries(3rdparty_ffmpeg INTERFACE ${FFMPEG_LIBRARIES})
|
||||
else()
|
||||
set(FFMPEG_PLATFORM_DIR "ffmpeg/${PLATFORM_ARCH}")
|
||||
|
||||
if (NOT MSVC AND WIN32)
|
||||
message("-- RPCS3: building ffmpeg submodule")
|
||||
|
||||
ExternalProject_Add(ffmpeg-mingw
|
||||
DOWNLOAD_COMMAND ""
|
||||
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg
|
||||
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/ffmpeg
|
||||
CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/ffmpeg/configure --prefix=./Windows/x86_64 --arch=x86_64 --disable-avdevice --disable-programs --disable-avfilter --disable-postproc --disable-doc --disable-pthreads --enable-w32threads --disable-network --disable-everything --disable-encoders --disable-muxers --disable-hwaccels --disable-parsers --disable-protocols --enable-dxva2 --enable-static --disable-shared --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=atrac3 --enable-decoder=atrac3p --enable-decoder=mp3 --enable-decoder=pcm_s16le --enable-decoder=pcm_s8 --enable-decoder=h264 --enable-decoder=mpeg4 --enable-decoder=mpeg2video --enable-decoder=mjpeg --enable-decoder=mjpegb --enable-encoder=pcm_s16le --enable-encoder=ffv1 --enable-encoder=mpeg4 --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=mpegvideo --enable-parser=mjpeg --enable-parser=aac --enable-parser=aac_latm --enable-muxer=avi --enable-demuxer=h264 --enable-demuxer=m4v --enable-demuxer=mp3 --enable-demuxer=mpegvideo --enable-demuxer=mpegps --enable-demuxer=mjpeg --enable-demuxer=avi --enable-demuxer=aac --enable-demuxer=pmp --enable-demuxer=oma --enable-demuxer=pcm_s16le --enable-demuxer=pcm_s8 --enable-demuxer=wav --enable-hwaccel=h264_dxva2 --enable-indev=dshow --enable-protocol=file
|
||||
BUILD_COMMAND make -j 4
|
||||
INSTALL_COMMAND make install
|
||||
)
|
||||
|
||||
set(FFMPEG_LIB_AVFORMAT "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libavformat.a")
|
||||
set(FFMPEG_LIB_AVCODEC "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libavcodec.a")
|
||||
set(FFMPEG_LIB_AVUTIL "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libavutil.a")
|
||||
set(FFMPEG_LIB_SWSCALE "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/${PLATFORM_ARCH}/lib/libswscale.a")
|
||||
else()
|
||||
message("-- RPCS3: using builtin ffmpeg")
|
||||
|
||||
set(FFMPEG_LIB_DIR "${FFMPEG_PLATFORM_DIR}/lib")
|
||||
find_library(FFMPEG_LIB_AVFORMAT avformat PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
find_library(FFMPEG_LIB_AVCODEC avcodec PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
find_library(FFMPEG_LIB_AVUTIL avutil PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
find_library(FFMPEG_LIB_SWSCALE swscale PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
|
||||
endif()
|
||||
|
||||
target_include_directories(3rdparty_ffmpeg INTERFACE "${FFMPEG_PLATFORM_DIR}/include")
|
||||
|
||||
target_link_libraries(3rdparty_ffmpeg
|
||||
INTERFACE
|
||||
${FFMPEG_LIB_AVFORMAT}
|
||||
${FFMPEG_LIB_AVCODEC}
|
||||
${FFMPEG_LIB_AVUTIL}
|
||||
${FFMPEG_LIB_SWSCALE})
|
||||
endif()
|
||||
|
||||
|
||||
# dx12
|
||||
add_library(3rdparty_dx12 INTERFACE)
|
||||
if (WIN32)
|
||||
target_link_libraries(3rdparty_dx12 INTERFACE dxgi.lib d2d1.lib dwrite.lib)
|
||||
endif()
|
||||
|
||||
|
||||
# GLEW
|
||||
add_library(3rdparty_glew INTERFACE)
|
||||
if(NOT MSVC)
|
||||
find_package(GLEW 1.13.0 REQUIRED)
|
||||
target_link_libraries(3rdparty_glew INTERFACE GLEW::GLEW)
|
||||
endif()
|
||||
|
||||
|
||||
# LLVM
|
||||
include(llvm.cmake)
|
||||
|
||||
|
||||
# add nice ALIAS targets for ease of use
|
||||
add_library(3rdparty::libusb ALIAS usb-1.0-static)
|
||||
add_library(3rdparty::zlib ALIAS 3rdparty_zlib)
|
||||
add_library(3rdparty::pugixml ALIAS pugixml)
|
||||
add_library(3rdparty::yaml-cpp ALIAS yaml-cpp)
|
||||
add_library(3rdparty::xxhash ALIAS xxhash)
|
||||
add_library(3rdparty::hidapi ALIAS 3rdparty_hidapi)
|
||||
add_library(3rdparty::libpng ALIAS ${LIBPNG_TARGET})
|
||||
add_library(3rdparty::cereal ALIAS 3rdparty_cereal)
|
||||
add_library(3rdparty::opengl ALIAS 3rdparty_opengl)
|
||||
add_library(3rdparty::gsl ALIAS 3rdparty_gsl)
|
||||
add_library(3rdparty::stblib ALIAS 3rdparty_stblib)
|
||||
add_library(3rdparty::discord-rpc ALIAS 3rdparty_discord-rpc)
|
||||
add_library(3rdparty::alsa ALIAS ${ALSA_TARGET})
|
||||
add_library(3rdparty::pulse ALIAS ${PULSE_TARGET})
|
||||
add_library(3rdparty::libevdev ALIAS ${LIBEVDEV_TARGET})
|
||||
add_library(3rdparty::vulkan ALIAS ${VULKAN_TARGET})
|
||||
add_library(3rdparty::openal ALIAS 3rdparty_openal)
|
||||
add_library(3rdparty::ffmpeg ALIAS 3rdparty_ffmpeg)
|
||||
add_library(3rdparty::dx12 ALIAS 3rdparty_dx12)
|
||||
add_library(3rdparty::glew ALIAS 3rdparty_glew)
|
||||
Vendored
+25
-699
File diff suppressed because it is too large
Load Diff
-16763
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
Submodule 3rdparty/GSL updated: 1995e86d1a...fc5fce4f4f
Vendored
+2
-118
@@ -13,8 +13,8 @@
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
* Or go to http://www.gnu.org/copyleft/lgpl.html
|
||||
*/
|
||||
|
||||
@@ -97,31 +97,6 @@ extern "C" {
|
||||
|
||||
#ifndef AL_EXT_MCFORMATS
|
||||
#define AL_EXT_MCFORMATS 1
|
||||
/* Provides support for surround sound buffer formats with 8, 16, and 32-bit
|
||||
* samples.
|
||||
*
|
||||
* QUAD8: Unsigned 8-bit, Quadraphonic (Front Left, Front Right, Rear Left,
|
||||
* Rear Right).
|
||||
* QUAD16: Signed 16-bit, Quadraphonic.
|
||||
* QUAD32: 32-bit float, Quadraphonic.
|
||||
* REAR8: Unsigned 8-bit, Rear Stereo (Rear Left, Rear Right).
|
||||
* REAR16: Signed 16-bit, Rear Stereo.
|
||||
* REAR32: 32-bit float, Rear Stereo.
|
||||
* 51CHN8: Unsigned 8-bit, 5.1 Surround (Front Left, Front Right, Front Center,
|
||||
* LFE, Side Left, Side Right). Note that some audio systems may label
|
||||
* 5.1's Side channels as Rear or Surround; they are equivalent for the
|
||||
* purposes of this extension.
|
||||
* 51CHN16: Signed 16-bit, 5.1 Surround.
|
||||
* 51CHN32: 32-bit float, 5.1 Surround.
|
||||
* 61CHN8: Unsigned 8-bit, 6.1 Surround (Front Left, Front Right, Front Center,
|
||||
* LFE, Rear Center, Side Left, Side Right).
|
||||
* 61CHN16: Signed 16-bit, 6.1 Surround.
|
||||
* 61CHN32: 32-bit float, 6.1 Surround.
|
||||
* 71CHN8: Unsigned 8-bit, 7.1 Surround (Front Left, Front Right, Front Center,
|
||||
* LFE, Rear Left, Rear Right, Side Left, Side Right).
|
||||
* 71CHN16: Signed 16-bit, 7.1 Surround.
|
||||
* 71CHN32: 32-bit float, 7.1 Surround.
|
||||
*/
|
||||
#define AL_FORMAT_QUAD8 0x1204
|
||||
#define AL_FORMAT_QUAD16 0x1205
|
||||
#define AL_FORMAT_QUAD32 0x1206
|
||||
@@ -418,97 +393,6 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AL_EXT_BFORMAT
|
||||
#define AL_EXT_BFORMAT 1
|
||||
/* Provides support for B-Format ambisonic buffers (first-order, FuMa scaling
|
||||
* and layout).
|
||||
*
|
||||
* BFORMAT2D_8: Unsigned 8-bit, 3-channel non-periphonic (WXY).
|
||||
* BFORMAT2D_16: Signed 16-bit, 3-channel non-periphonic (WXY).
|
||||
* BFORMAT2D_FLOAT32: 32-bit float, 3-channel non-periphonic (WXY).
|
||||
* BFORMAT3D_8: Unsigned 8-bit, 4-channel periphonic (WXYZ).
|
||||
* BFORMAT3D_16: Signed 16-bit, 4-channel periphonic (WXYZ).
|
||||
* BFORMAT3D_FLOAT32: 32-bit float, 4-channel periphonic (WXYZ).
|
||||
*/
|
||||
#define AL_FORMAT_BFORMAT2D_8 0x20021
|
||||
#define AL_FORMAT_BFORMAT2D_16 0x20022
|
||||
#define AL_FORMAT_BFORMAT2D_FLOAT32 0x20023
|
||||
#define AL_FORMAT_BFORMAT3D_8 0x20031
|
||||
#define AL_FORMAT_BFORMAT3D_16 0x20032
|
||||
#define AL_FORMAT_BFORMAT3D_FLOAT32 0x20033
|
||||
#endif
|
||||
|
||||
#ifndef AL_EXT_MULAW_BFORMAT
|
||||
#define AL_EXT_MULAW_BFORMAT 1
|
||||
#define AL_FORMAT_BFORMAT2D_MULAW 0x10031
|
||||
#define AL_FORMAT_BFORMAT3D_MULAW 0x10032
|
||||
#endif
|
||||
|
||||
#ifndef ALC_SOFT_HRTF
|
||||
#define ALC_SOFT_HRTF 1
|
||||
#define ALC_HRTF_SOFT 0x1992
|
||||
#define ALC_DONT_CARE_SOFT 0x0002
|
||||
#define ALC_HRTF_STATUS_SOFT 0x1993
|
||||
#define ALC_HRTF_DISABLED_SOFT 0x0000
|
||||
#define ALC_HRTF_ENABLED_SOFT 0x0001
|
||||
#define ALC_HRTF_DENIED_SOFT 0x0002
|
||||
#define ALC_HRTF_REQUIRED_SOFT 0x0003
|
||||
#define ALC_HRTF_HEADPHONES_DETECTED_SOFT 0x0004
|
||||
#define ALC_HRTF_UNSUPPORTED_FORMAT_SOFT 0x0005
|
||||
#define ALC_NUM_HRTF_SPECIFIERS_SOFT 0x1994
|
||||
#define ALC_HRTF_SPECIFIER_SOFT 0x1995
|
||||
#define ALC_HRTF_ID_SOFT 0x1996
|
||||
typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index);
|
||||
typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs);
|
||||
#ifdef AL_ALEXT_PROTOTYPES
|
||||
ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index);
|
||||
ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AL_SOFT_gain_clamp_ex
|
||||
#define AL_SOFT_gain_clamp_ex 1
|
||||
#define AL_GAIN_LIMIT_SOFT 0x200E
|
||||
#endif
|
||||
|
||||
#ifndef AL_SOFT_source_resampler
|
||||
#define AL_SOFT_source_resampler
|
||||
#define AL_NUM_RESAMPLERS_SOFT 0x1210
|
||||
#define AL_DEFAULT_RESAMPLER_SOFT 0x1211
|
||||
#define AL_SOURCE_RESAMPLER_SOFT 0x1212
|
||||
#define AL_RESAMPLER_NAME_SOFT 0x1213
|
||||
typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index);
|
||||
#ifdef AL_ALEXT_PROTOTYPES
|
||||
AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef AL_SOFT_source_spatialize
|
||||
#define AL_SOFT_source_spatialize
|
||||
#define AL_SOURCE_SPATIALIZE_SOFT 0x1214
|
||||
#define AL_AUTO_SOFT 0x0002
|
||||
#endif
|
||||
|
||||
#ifndef ALC_SOFT_output_limiter
|
||||
#define ALC_SOFT_output_limiter
|
||||
#define ALC_OUTPUT_LIMITER_SOFT 0x199A
|
||||
#endif
|
||||
|
||||
#ifndef ALC_SOFT_device_clock
|
||||
#define ALC_SOFT_device_clock 1
|
||||
typedef int64_t ALCint64SOFT;
|
||||
typedef uint64_t ALCuint64SOFT;
|
||||
#define ALC_DEVICE_CLOCK_SOFT 0x1600
|
||||
#define ALC_DEVICE_LATENCY_SOFT 0x1601
|
||||
#define ALC_DEVICE_CLOCK_LATENCY_SOFT 0x1602
|
||||
#define AL_SAMPLE_OFFSET_CLOCK_SOFT 0x1202
|
||||
#define AL_SEC_OFFSET_CLOCK_SOFT 0x1203
|
||||
typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
|
||||
#ifdef AL_ALEXT_PROTOTYPES
|
||||
ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -345,7 +345,7 @@ typedef struct {
|
||||
/* Driving Presets */
|
||||
|
||||
#define EFX_REVERB_PRESET_DRIVING_COMMENTATOR \
|
||||
{ 1.0000f, 0.0000f, 0.3162f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
|
||||
{ 1.0000f, 0.0000f, 3.1623f, 0.5623f, 0.5012f, 2.4200f, 0.8800f, 0.6800f, 0.1995f, 0.0930f, { 0.0000f, 0.0000f, 0.0000f }, 0.2512f, 0.0170f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 1.0000f, 0.2500f, 0.0000f, 0.9886f, 5000.0000f, 250.0000f, 0.0000f, 0x1 }
|
||||
|
||||
#define EFX_REVERB_PRESET_DRIVING_PITGARAGE \
|
||||
{ 0.4287f, 0.5900f, 0.3162f, 0.7079f, 0.5623f, 1.7200f, 0.9300f, 0.8700f, 0.5623f, 0.0000f, { 0.0000f, 0.0000f, 0.0000f }, 1.2589f, 0.0160f, { 0.0000f, 0.0000f, 0.0000f }, 0.2500f, 0.1100f, 0.2500f, 0.0000f, 0.9943f, 5000.0000f, 250.0000f, 0.0000f, 0x0 }
|
||||
|
||||
+91
@@ -1,35 +1,84 @@
|
||||
EXPORTS
|
||||
alAuxiliaryEffectSlotf
|
||||
alAuxiliaryEffectSlotfv
|
||||
alAuxiliaryEffectSloti
|
||||
alAuxiliaryEffectSlotiv
|
||||
alBuffer3f
|
||||
alBuffer3i
|
||||
alBufferData
|
||||
alBufferSamplesSOFT
|
||||
alBufferSubDataSOFT
|
||||
alBufferSubSamplesSOFT
|
||||
alBufferf
|
||||
alBufferfv
|
||||
alBufferi
|
||||
alBufferiv
|
||||
alDeferUpdatesSOFT
|
||||
alDeleteAuxiliaryEffectSlots
|
||||
alDeleteBuffers
|
||||
alDeleteEffects
|
||||
alDeleteFilters
|
||||
alDeleteFontsoundsSOFT
|
||||
alDeletePresetsSOFT
|
||||
alDeleteSoundfontsSOFT
|
||||
alDeleteSources
|
||||
alDisable
|
||||
alDistanceModel
|
||||
alDopplerFactor
|
||||
alDopplerVelocity
|
||||
alEffectf
|
||||
alEffectfv
|
||||
alEffecti
|
||||
alEffectiv
|
||||
alEnable
|
||||
alFilterf
|
||||
alFilterfv
|
||||
alFilteri
|
||||
alFilteriv
|
||||
alFontsound2iSOFT
|
||||
alFontsoundModulatoriSOFT
|
||||
alFontsoundiSOFT
|
||||
alFontsoundivSOFT
|
||||
alGenAuxiliaryEffectSlots
|
||||
alGenBuffers
|
||||
alGenEffects
|
||||
alGenFilters
|
||||
alGenFontsoundsSOFT
|
||||
alGenPresetsSOFT
|
||||
alGenSoundfontsSOFT
|
||||
alGenSources
|
||||
alGetAuxiliaryEffectSlotf
|
||||
alGetAuxiliaryEffectSlotfv
|
||||
alGetAuxiliaryEffectSloti
|
||||
alGetAuxiliaryEffectSlotiv
|
||||
alGetBoolean
|
||||
alGetBooleanv
|
||||
alGetBuffer3f
|
||||
alGetBuffer3i
|
||||
alGetBufferSamplesSOFT
|
||||
alGetBufferf
|
||||
alGetBufferfv
|
||||
alGetBufferi
|
||||
alGetBufferiv
|
||||
alGetDouble
|
||||
alGetDoublev
|
||||
alGetEffectf
|
||||
alGetEffectfv
|
||||
alGetEffecti
|
||||
alGetEffectiv
|
||||
alGetEnumValue
|
||||
alGetError
|
||||
alGetFilterf
|
||||
alGetFilterfv
|
||||
alGetFilteri
|
||||
alGetFilteriv
|
||||
alGetFloat
|
||||
alGetFloatv
|
||||
alGetFontsoundModulatorivSOFT
|
||||
alGetFontsoundivSOFT
|
||||
alGetInteger
|
||||
alGetInteger64SOFT
|
||||
alGetInteger64vSOFT
|
||||
alGetIntegerv
|
||||
alGetListener3f
|
||||
alGetListener3i
|
||||
@@ -37,17 +86,32 @@ EXPORTS
|
||||
alGetListenerfv
|
||||
alGetListeneri
|
||||
alGetListeneriv
|
||||
alGetPresetivSOFT
|
||||
alGetProcAddress
|
||||
alGetSoundfontivSOFT
|
||||
alGetSource3dSOFT
|
||||
alGetSource3f
|
||||
alGetSource3i
|
||||
alGetSource3i64SOFT
|
||||
alGetSourcedSOFT
|
||||
alGetSourcedvSOFT
|
||||
alGetSourcef
|
||||
alGetSourcefv
|
||||
alGetSourcei
|
||||
alGetSourcei64SOFT
|
||||
alGetSourcei64vSOFT
|
||||
alGetSourceiv
|
||||
alGetString
|
||||
alIsAuxiliaryEffectSlot
|
||||
alIsBuffer
|
||||
alIsBufferFormatSupportedSOFT
|
||||
alIsEffect
|
||||
alIsEnabled
|
||||
alIsExtensionPresent
|
||||
alIsFilter
|
||||
alIsFontsoundSOFT
|
||||
alIsPresetSOFT
|
||||
alIsSoundfontSOFT
|
||||
alIsSource
|
||||
alListener3f
|
||||
alListener3i
|
||||
@@ -55,8 +119,25 @@ EXPORTS
|
||||
alListenerfv
|
||||
alListeneri
|
||||
alListeneriv
|
||||
alLoadSoundfontSOFT
|
||||
alMidiEventSOFT
|
||||
alMidiGainSOFT
|
||||
alMidiPauseSOFT
|
||||
alMidiPlaySOFT
|
||||
alMidiResetSOFT
|
||||
alMidiSoundfontSOFT
|
||||
alMidiSoundfontvSOFT
|
||||
alMidiStopSOFT
|
||||
alMidiSysExSOFT
|
||||
alPresetFontsoundsSOFT
|
||||
alPresetiSOFT
|
||||
alPresetivSOFT
|
||||
alProcessUpdatesSOFT
|
||||
alSoundfontPresetsSOFT
|
||||
alSource3dSOFT
|
||||
alSource3f
|
||||
alSource3i
|
||||
alSource3i64SOFT
|
||||
alSourcePause
|
||||
alSourcePausev
|
||||
alSourcePlay
|
||||
@@ -67,9 +148,13 @@ EXPORTS
|
||||
alSourceStop
|
||||
alSourceStopv
|
||||
alSourceUnqueueBuffers
|
||||
alSourcedSOFT
|
||||
alSourcedvSOFT
|
||||
alSourcef
|
||||
alSourcefv
|
||||
alSourcei
|
||||
alSourcei64SOFT
|
||||
alSourcei64vSOFT
|
||||
alSourceiv
|
||||
alSpeedOfSound
|
||||
alcCaptureCloseDevice
|
||||
@@ -80,17 +165,23 @@ EXPORTS
|
||||
alcCloseDevice
|
||||
alcCreateContext
|
||||
alcDestroyContext
|
||||
alcDevicePauseSOFT
|
||||
alcDeviceResumeSOFT
|
||||
alcGetContextsDevice
|
||||
alcGetCurrentContext
|
||||
alcGetEnumValue
|
||||
alcGetError
|
||||
alcGetInteger64vSOFT
|
||||
alcGetIntegerv
|
||||
alcGetProcAddress
|
||||
alcGetString
|
||||
alcGetThreadContext
|
||||
alcIsExtensionPresent
|
||||
alcIsRenderFormatSupportedSOFT
|
||||
alcLoopbackOpenDeviceSOFT
|
||||
alcMakeContextCurrent
|
||||
alcOpenDevice
|
||||
alcProcessContext
|
||||
alcRenderSamplesSOFT
|
||||
alcSetThreadContext
|
||||
alcSuspendContext
|
||||
|
||||
BIN
Binary file not shown.
-26
@@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(DISCORD_DYNAMIC_LIB)
|
||||
# if defined(_WIN32)
|
||||
# if defined(DISCORD_BUILDING_SDK)
|
||||
# define DISCORD_EXPORT __declspec(dllexport)
|
||||
# else
|
||||
# define DISCORD_EXPORT __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define DISCORD_EXPORT __attribute__((visibility("default")))
|
||||
# endif
|
||||
#else
|
||||
# define DISCORD_EXPORT
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DISCORD_EXPORT void Discord_Register(const char* applicationId, const char* command);
|
||||
DISCORD_EXPORT void Discord_RegisterSteamGame(const char* applicationId, const char* steamId);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
-87
@@ -1,87 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
// clang-format off
|
||||
|
||||
#if defined(DISCORD_DYNAMIC_LIB)
|
||||
# if defined(_WIN32)
|
||||
# if defined(DISCORD_BUILDING_SDK)
|
||||
# define DISCORD_EXPORT __declspec(dllexport)
|
||||
# else
|
||||
# define DISCORD_EXPORT __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define DISCORD_EXPORT __attribute__((visibility("default")))
|
||||
# endif
|
||||
#else
|
||||
# define DISCORD_EXPORT
|
||||
#endif
|
||||
|
||||
// clang-format on
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct DiscordRichPresence {
|
||||
const char* state; /* max 128 bytes */
|
||||
const char* details; /* max 128 bytes */
|
||||
int64_t startTimestamp;
|
||||
int64_t endTimestamp;
|
||||
const char* largeImageKey; /* max 32 bytes */
|
||||
const char* largeImageText; /* max 128 bytes */
|
||||
const char* smallImageKey; /* max 32 bytes */
|
||||
const char* smallImageText; /* max 128 bytes */
|
||||
const char* partyId; /* max 128 bytes */
|
||||
int partySize;
|
||||
int partyMax;
|
||||
const char* matchSecret; /* max 128 bytes */
|
||||
const char* joinSecret; /* max 128 bytes */
|
||||
const char* spectateSecret; /* max 128 bytes */
|
||||
int8_t instance;
|
||||
} DiscordRichPresence;
|
||||
|
||||
typedef struct DiscordJoinRequest {
|
||||
const char* userId;
|
||||
const char* username;
|
||||
const char* discriminator;
|
||||
const char* avatar;
|
||||
} DiscordJoinRequest;
|
||||
|
||||
typedef struct DiscordEventHandlers {
|
||||
void (*ready)(void);
|
||||
void (*disconnected)(int errorCode, const char* message);
|
||||
void (*errored)(int errorCode, const char* message);
|
||||
void (*joinGame)(const char* joinSecret);
|
||||
void (*spectateGame)(const char* spectateSecret);
|
||||
void (*joinRequest)(const DiscordJoinRequest* request);
|
||||
} DiscordEventHandlers;
|
||||
|
||||
#define DISCORD_REPLY_NO 0
|
||||
#define DISCORD_REPLY_YES 1
|
||||
#define DISCORD_REPLY_IGNORE 2
|
||||
|
||||
DISCORD_EXPORT void Discord_Initialize(const char* applicationId,
|
||||
DiscordEventHandlers* handlers,
|
||||
int autoRegister,
|
||||
const char* optionalSteamId);
|
||||
DISCORD_EXPORT void Discord_Shutdown(void);
|
||||
|
||||
/* checks for incoming messages, dispatches callbacks */
|
||||
DISCORD_EXPORT void Discord_RunCallbacks(void);
|
||||
|
||||
/* If you disable the lib starting its own io thread, you'll need to call this from your own */
|
||||
#ifdef DISCORD_DISABLE_IO_THREAD
|
||||
DISCORD_EXPORT void Discord_UpdateConnection(void);
|
||||
#endif
|
||||
|
||||
DISCORD_EXPORT void Discord_UpdatePresence(const DiscordRichPresence* presence);
|
||||
DISCORD_EXPORT void Discord_ClearPresence(void);
|
||||
|
||||
DISCORD_EXPORT void Discord_Respond(const char* userid, /* DISCORD_REPLY_ */ int reply);
|
||||
|
||||
DISCORD_EXPORT void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
+1
-1
Submodule 3rdparty/ffmpeg updated: 7b7ae7b067...8dd84dc1ea
Vendored
+1
-1
Submodule 3rdparty/hidapi updated: 9220f5e77c...66eed15b05
Vendored
-105
@@ -1,105 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{A107C21C-418A-4697-BB10-20C3AA60E2E4}</ProjectGuid>
|
||||
<RootNamespace>hidapi</RootNamespace>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>15.0.26323.1</_ProjectFileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>hidapi\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>setupapi.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>hidapi\hidapi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>setupapi.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="hidapi\windows\hid.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="hidapi\hidapi\hidapi.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
-27
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="hidapi\windows\hid.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="hidapi\hidapi\hidapi.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Vendored
+1
-1
Submodule 3rdparty/libpng updated: eddf902320...bbbcaf1122
Vendored
-142
@@ -1,142 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug Library|x64">
|
||||
<Configuration>Debug Library</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release Library|x64">
|
||||
<Configuration>Release Library</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{D6973076-9317-4EF2-A0B8-B7A18AC0713E}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>libpng</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\3rdparty\zlib.props" />
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<CustomBuildBeforeTargets />
|
||||
<TargetName>$(ProjectName)16</TargetName>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<CustomBuildBeforeTargets />
|
||||
<TargetName>$(ProjectName)16</TargetName>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<FloatingPointExceptions>false</FloatingPointExceptions>
|
||||
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
|
||||
<PrecompiledHeaderFile>pngpriv.h</PrecompiledHeaderFile>
|
||||
<BrowseInformation>true</BrowseInformation>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<StringPooling>true</StringPooling>
|
||||
<DisableSpecificWarnings>$(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>$(ZLibSrcDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FloatingPointExceptions>false</FloatingPointExceptions>
|
||||
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
|
||||
<PrecompiledHeaderFile>pngpriv.h</PrecompiledHeaderFile>
|
||||
<BrowseInformation>true</BrowseInformation>
|
||||
<CompileAs>CompileAsC</CompileAs>
|
||||
<StringPooling>true</StringPooling>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<DisableSpecificWarnings>$(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<AdditionalIncludeDirectories>$(ZLibSrcDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
|
||||
<Optimization>Full</Optimization>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<Lib>
|
||||
<LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include=".\libpng\png.c">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include=".\libpng\pngerror.c" />
|
||||
<ClCompile Include=".\libpng\pngget.c" />
|
||||
<ClCompile Include=".\libpng\pngmem.c" />
|
||||
<ClCompile Include=".\libpng\pngpread.c" />
|
||||
<ClCompile Include=".\libpng\pngread.c" />
|
||||
<ClCompile Include=".\libpng\pngrio.c" />
|
||||
<ClCompile Include=".\libpng\pngrtran.c" />
|
||||
<ClCompile Include=".\libpng\pngrutil.c" />
|
||||
<ClCompile Include=".\libpng\pngset.c" />
|
||||
<ClCompile Include=".\libpng\pngtrans.c" />
|
||||
<ClCompile Include=".\libpng\pngwio.c" />
|
||||
<ClCompile Include=".\libpng\pngwrite.c" />
|
||||
<ClCompile Include=".\libpng\pngwtran.c" />
|
||||
<ClCompile Include=".\libpng\pngwutil.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include=".\libpng\scripts\pngwin.rc">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Library|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release Library|x64'">true</ExcludedFromBuild>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
-1
Submodule 3rdparty/libusb deleted from 7cfa00e9d7
Vendored
-90
@@ -1,90 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectName>libusb-1.0 (static)</ProjectName>
|
||||
<ProjectGuid>{349EE8F9-7D25-4909-AAF5-FF3FADE72187}</ProjectGuid>
|
||||
<RootNamespace>libusb</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<WholeProgramOptimization Condition="'$(Configuration)'=='Release'">true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
|
||||
<OutDir>$(SolutionDir)lib/</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)/</IntDir>
|
||||
<TargetName>libusb-1.0</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>libusb\msvc;libusb\libusb;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WINVER=0x0501;_WIN32_WINNT=0x0501;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
</ClCompile>
|
||||
<ClCompile Condition="'$(Configuration)'=='Debug'">
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<ClCompile Condition="'$(Configuration)'=='Release'">
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Lib>
|
||||
<OutputFile>$(OutDir)libusb-1.0.lib</OutputFile>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="libusb\libusb\core.c" />
|
||||
<ClCompile Include="libusb\libusb\descriptor.c" />
|
||||
<ClCompile Include="libusb\libusb\hotplug.c" />
|
||||
<ClCompile Include="libusb\libusb\io.c" />
|
||||
<ClCompile Include="libusb\libusb\os\poll_windows.c" />
|
||||
<ClCompile Include="libusb\libusb\strerror.c" />
|
||||
<ClCompile Include="libusb\libusb\sync.c" />
|
||||
<ClCompile Include="libusb\libusb\os\threads_windows.c" />
|
||||
<ClCompile Include="libusb\libusb\os\windows_nt_common.c" />
|
||||
<ClCompile Include="libusb\libusb\os\windows_usbdk.c" />
|
||||
<ClCompile Include="libusb\libusb\os\windows_winusb.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="libusb\msvc\config.h" />
|
||||
<ClInclude Include="libusb\libusb\hotplug.h" />
|
||||
<ClInclude Include="libusb\libusb\libusb.h" />
|
||||
<ClInclude Include="libusb\libusb\libusbi.h" />
|
||||
<ClInclude Include="libusb\libusb\os\poll_windows.h" />
|
||||
<ClInclude Include="libusb\libusb\os\threads_windows.h" />
|
||||
<ClInclude Include="libusb\libusb\version.h" />
|
||||
<ClInclude Include="libusb\libusb\version_nano.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_common.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_nt_common.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_nt_shared_types.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_usbdk.h" />
|
||||
<ClInclude Include="libusb\libusb\os\windows_winusb.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
-65
@@ -1,65 +0,0 @@
|
||||
if(WITH_LLVM)
|
||||
if(BUILD_LLVM_SUBMODULE)
|
||||
message(STATUS "LLVM will be built from the submodule.")
|
||||
|
||||
set(LLVM_TARGETS_TO_BUILD "X86" CACHE INTERNAL "")
|
||||
option(LLVM_BUILD_RUNTIME OFF)
|
||||
option(LLVM_BUILD_TOOLS OFF)
|
||||
option(LLVM_INCLUDE_DOCS OFF)
|
||||
option(LLVM_INCLUDE_EXAMPLES OFF)
|
||||
option(LLVM_INCLUDE_TESTS OFF)
|
||||
option(LLVM_INCLUDE_TOOLS OFF)
|
||||
option(LLVM_INCLUDE_UTILS OFF)
|
||||
option(WITH_POLLY OFF)
|
||||
option(LLVM_ENABLE_CXX1Z TRUE)
|
||||
|
||||
set(CXX_FLAGS_OLD ${CMAKE_CXX_FLAGS})
|
||||
|
||||
if (MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS")
|
||||
endif()
|
||||
|
||||
# LLVM needs to be built out-of-tree
|
||||
add_subdirectory(${CMAKE_SOURCE_DIR}/llvm ${CMAKE_CURRENT_BINARY_DIR}/llvm_build EXCLUDE_FROM_ALL)
|
||||
set(LLVM_DIR "${CMAKE_CURRENT_BINARY_DIR}/llvm_build/lib/cmake/llvm/")
|
||||
|
||||
set(CMAKE_CXX_FLAGS ${CXX_FLAGS_OLD})
|
||||
|
||||
# now tries to find LLVM again
|
||||
find_package(LLVM 9.0 CONFIG)
|
||||
if(NOT LLVM_FOUND)
|
||||
message(FATAL_ERROR "Couldn't build LLVM from the submodule. You might need to run `git submodule update --init`")
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(STATUS "Using prebuilt LLVM")
|
||||
|
||||
if (LLVM_DIR AND NOT IS_ABSOLUTE "${LLVM_DIR}")
|
||||
# change relative LLVM_DIR to be relative to the source dir
|
||||
set(LLVM_DIR ${CMAKE_SOURCE_DIR}/${LLVM_DIR})
|
||||
endif()
|
||||
|
||||
find_package(LLVM 9.0 CONFIG)
|
||||
|
||||
if (NOT LLVM_FOUND)
|
||||
if (LLVM_VERSION AND LLVM_VERSION_MAJOR LESS 9)
|
||||
message(FATAL_ERROR "Found LLVM version ${LLVM_VERSION}. Required version 9.0. \
|
||||
Enable BUILD_LLVM_SUBMODULE option to build LLVM from included as a git submodule.")
|
||||
endif()
|
||||
|
||||
message(FATAL_ERROR "Can't find LLVM libraries from the CMAKE_PREFIX_PATH path or LLVM_DIR. \
|
||||
Enable BUILD_LLVM_SUBMODULE option to build LLVM from included as a git submodule.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(LLVM_LIBS LLVMMCJIT LLVMX86CodeGen LLVMX86AsmParser)
|
||||
|
||||
add_library(3rdparty_llvm INTERFACE)
|
||||
target_link_libraries(3rdparty_llvm INTERFACE ${LLVM_LIBS})
|
||||
target_include_directories(3rdparty_llvm INTERFACE ${LLVM_INCLUDE_DIRS})
|
||||
target_compile_definitions(3rdparty_llvm INTERFACE ${LLVM_DEFINITIONS} -DLLVM_AVAILABLE)
|
||||
|
||||
add_library(3rdparty::llvm ALIAS 3rdparty_llvm)
|
||||
else()
|
||||
add_library(3rdparty::llvm ALIAS 3rdparty_dummy_lib)
|
||||
endif()
|
||||
Vendored
-64
@@ -1,64 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{EB33566E-DA7F-4D28-9077-88C0B7C77E35}</ProjectGuid>
|
||||
<RootNamespace>pnglibconf</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\3rdparty\zlib.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<CustomBuildBeforeTargets>Build</CustomBuildBeforeTargets>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<CustomBuildStep>
|
||||
<Command>copy .\libpng\scripts\pnglibconf.h.prebuilt .\libpng\pnglibconf.h</Command>
|
||||
</CustomBuildStep>
|
||||
<CustomBuildStep>
|
||||
<Message>Generating pnglibconf.h</Message>
|
||||
</CustomBuildStep>
|
||||
<CustomBuildStep>
|
||||
<Outputs>.\libpng\pnglibconf.h</Outputs>
|
||||
</CustomBuildStep>
|
||||
<CustomBuildStep>
|
||||
<Inputs>.\libpng\scripts\pnglibconf.h.prebuilt</Inputs>
|
||||
</CustomBuildStep>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
+1
-1
Submodule 3rdparty/pugixml updated: 8bf806c035...f205aaf6e1
Vendored
-49
@@ -1,49 +0,0 @@
|
||||
add_library(3rdparty_qt5 INTERFACE)
|
||||
|
||||
find_package(Qt5 5.10 CONFIG COMPONENTS Widgets Network Qml)
|
||||
if(WIN32)
|
||||
find_package(Qt5 5.10 COMPONENTS WinExtras REQUIRED)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Network Qt5::Qml)
|
||||
else()
|
||||
find_package(Qt5 5.10 COMPONENTS DBus Gui)
|
||||
if(Qt5DBus_FOUND)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Network Qt5::Qml)
|
||||
target_compile_definitions(3rdparty_qt5 INTERFACE -DHAVE_QTDBUS)
|
||||
else()
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Network Qt5::Qml)
|
||||
endif()
|
||||
target_include_directories(3rdparty_qt5 INTERFACE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
if(NOT Qt5Widgets_FOUND)
|
||||
if(Qt5Widgets_VERSION VERSION_LESS 5.10.0)
|
||||
message("Minimum supported Qt5 version is 5.10.0! You have version ${Qt5Widgets_VERSION} installed, please upgrade!")
|
||||
if(CMAKE_SYSTEM MATCHES "Linux")
|
||||
message(FATAL_ERROR "Most distros do not provide an up-to-date version of Qt.
|
||||
If you're on Ubuntu or Linux Mint, there are PPAs you can use to install one of the latest qt5 versions.
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.11.0-bionic
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.11.0-xenial
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.10.1-trusty
|
||||
just make sure to run
|
||||
source /opt/qt511/bin/qt511-env.sh
|
||||
respective
|
||||
source /opt/qt510/bin/qt510-env.sh
|
||||
before re-running cmake")
|
||||
elseif(WIN32)
|
||||
message(FATAL_ERROR "You can download the latest version of Qt5 here: https://www.qt.io/download-open-source/")
|
||||
else()
|
||||
message(FATAL_ERROR "Look online for instructions on installing an up-to-date Qt5 on ${CMAKE_SYSTEM}.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message("CMake was unable to find Qt5!")
|
||||
if(WIN32)
|
||||
message(FATAL_ERROR "Make sure the QTDIR env variable has been set properly. (for example C:\\Qt\\5.11.1\\msvc2017_64\\)")
|
||||
elseif(CMAKE_SYSTEM MATCHES "Linux")
|
||||
message(FATAL_ERROR "Make sure to install your distro's qt5 package!")
|
||||
else()
|
||||
message(FATAL_ERROR "You need to have Qt5 installed, look online for instructions on installing Qt5 on ${CMAKE_SYSTEM}.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(3rdparty::qt5 ALIAS 3rdparty_qt5)
|
||||
Vendored
+4672
-5521
File diff suppressed because it is too large
Load Diff
Vendored
+2316
-3897
File diff suppressed because it is too large
Load Diff
Vendored
-1
Submodule 3rdparty/xxHash deleted from 7cc9639699
Vendored
-59
@@ -1,59 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{939FE206-1182-ABC3-1234-FEAB88E98404}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\rpcs3_default.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="..\rpcs3_debug.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="..\rpcs3_release.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup />
|
||||
<ItemDefinitionGroup>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="xxHash/xxhash.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="LICENSE" />
|
||||
<Text Include="CMakeLists.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="xxHash/xxhash.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
Vendored
-1
Submodule 3rdparty/yaml-cpp deleted from eca9cfd648
Vendored
-105
@@ -1,105 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(ZLibSrcDir)\adler32.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\compress.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\crc32.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\deflate.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\infback.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\inffast.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\inflate.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\inftrees.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\trees.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\uncompr.c" />
|
||||
<ClCompile Include="$(ZLibSrcDir)\zutil.c" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{60F89955-91C6-3A36-8000-13C592FEC2DF}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>zlib</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\3rdparty\zlib.props" />
|
||||
<Import Project="..\common_default.props" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<Import Project="..\common_default_macros.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
|
||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<BrowseInformation>true</BrowseInformation>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<DisableSpecificWarnings>$(DisableSpecificWarnings);4127;4131;4242;4244</DisableSpecificWarnings>
|
||||
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>$(WarningLevel)</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Full</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<BufferSecurityCheck>false</BufferSecurityCheck>
|
||||
<BrowseInformation>true</BrowseInformation>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<DisableSpecificWarnings>$(DisableSpecificWarnings);4127;4131;4242;4244</DisableSpecificWarnings>
|
||||
<TreatWarningAsError>$(TreatWarningAsError)</TreatWarningAsError>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<Lib>
|
||||
<LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
# Building
|
||||
|
||||
Only Windows and Linux are officially supported for building. However, various other platforms are capable of building RPCS3.
|
||||
Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Building).
|
||||
|
||||
## Setup your environment
|
||||
|
||||
### Windows 7 or later
|
||||
|
||||
* [CMake 3.8.2+](https://www.cmake.org/download/) (add to PATH)
|
||||
* [Python 3.3+](https://www.python.org/downloads/) (add to PATH)
|
||||
* [Qt 5.10+](https://www.qt.io/download-qt-installer) (Avoid 5.11.1, due to a bug)
|
||||
* [Visual Studio 2017](https://visualstudio.microsoft.com/vs/older-downloads/)
|
||||
* [Vulkan SDK 1.1.97.0+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html))
|
||||
|
||||
**Either add the** `QTDIR` **environment variable, e.g.** `<QtInstallFolder>\5.10.0\msvc2017_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools-19123)**
|
||||
|
||||
### Linux
|
||||
|
||||
These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager.
|
||||
|
||||
* Clang 5.0+ or GCC 8.1+
|
||||
* [CMake 3.8.2+](https://www.cmake.org/download/)
|
||||
* [Qt 5.10+](https://www.qt.io/download-qt-installer) (Avoid 5.11.1, due to a bug)
|
||||
* [Vulkan SDK 1.1.97.0+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html))
|
||||
|
||||
**If you have an NVIDIA GPU, you may need to install the libglvnd package.**
|
||||
|
||||
#### Arch Linux
|
||||
|
||||
sudo pacman -S glew openal cmake vulkan-validation-layers qt5-base qt5-declarative
|
||||
|
||||
#### Debian & Ubuntu
|
||||
|
||||
sudo apt-get install cmake build-essential libasound2-dev libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git qt5-default libevdev-dev qtdeclarative5-dev qtbase5-private-dev
|
||||
|
||||
##### GCC 8.x installation
|
||||
|
||||
If the `gcc-8` package is not available on your system, use the following command
|
||||
```
|
||||
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
|
||||
```
|
||||
Then
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-8 g++-8
|
||||
```
|
||||
|
||||
You can either use `update-alternatives` to setup `gcc-8`/`g++-8` as your default compilers or prefix any `cmake` command by `CXX=g++-8 CC=gcc-8 ` to use it.
|
||||
|
||||
##### Vulkan SDK
|
||||
|
||||
For Ubuntu systems, it is strongly recommended to use the PPA from [LunarG](https://packages.lunarg.com/) which will provide a compatible Vulkan SDK to compile RPCS3. If your Vulkan SDK is older, it can lead to compilation errors.
|
||||
|
||||
Ubuntu 18.04 (Bionic Beaver)
|
||||
```
|
||||
wget -qO - http://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
|
||||
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.1.106-bionic.list http://packages.lunarg.com/vulkan/1.1.106/lunarg-vulkan-1.1.106-bionic.list
|
||||
sudo apt update
|
||||
sudo apt install vulkan-sdk
|
||||
```
|
||||
|
||||
Ubuntu 16.04 (Xenial Xerus)
|
||||
```
|
||||
wget -qO - http://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
|
||||
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.1.106-xenial.list http://packages.lunarg.com/vulkan/1.1.106/lunarg-vulkan-1.1.106-xenial.list
|
||||
sudo apt update
|
||||
sudo apt install vulkan-sdk
|
||||
```
|
||||
|
||||
#### Fedora
|
||||
|
||||
sudo dnf install alsa-lib-devel cmake glew glew-devel libatomic libevdev-devel libudev-devel openal-devel qt5-devel vulkan-devel
|
||||
|
||||
#### OpenSUSE
|
||||
|
||||
sudo zypper install git cmake libasound2 libpulse-devel openal-soft-devel glew-devel zlib-devel libedit-devel vulkan-devel libudev-devel libqt5-qtbase-devel libevdev-devel
|
||||
|
||||
## Setup the project
|
||||
|
||||
Clone and initialize the repository
|
||||
|
||||
```
|
||||
git clone https://github.com/RPCS3/rpcs3.git
|
||||
cd rpcs3
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
#### Configuring the Qt plugin (if used)
|
||||
|
||||
1) Go to the Qt5 menu and edit Qt5 options.
|
||||
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\5.10.0\msvc2017_64`.
|
||||
3) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
|
||||
|
||||
#### Building the projects
|
||||
|
||||
Open `rpcs3.sln`. The recommended build configuration is `Release - LLVM` for all purposes.
|
||||
|
||||
You may want to download precompiled [LLVM libs](https://github.com/RPCS3/llvm/releases/download/continuous-master/llvmlibs.7z) and extract to root rpcs3 folder (which contains `rpcs3.sln`), as well as download and extract [additional libs](https://drive.google.com/uc?export=download&id=1A2eOMmCO714i0U7J0qI4aEMKnuWl8l_R) to `lib\%CONFIGURATION%-x64\` to speed up compilation time (unoptimised/debug libs are currently not available precompiled).
|
||||
|
||||
If you're not using precompiled libs, build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*.
|
||||
|
||||
`Build > Build Solution`
|
||||
|
||||
### Linux
|
||||
|
||||
While still in the project root:
|
||||
|
||||
1) `cd .. && mkdir rpcs3_build && cd rpcs3_build`
|
||||
2) `cmake ../rpcs3/ && make` or `CXX=g++-8 CC=gcc-8 cmake ../rpcs3/ && make` to force these compilers
|
||||
3) Run RPCS3 with `./bin/rpcs3`
|
||||
|
||||
When using GDB, configure it to ignore SIGSEGV signal (`handle SIGSEGV nostop noprint`).
|
||||
If desired, use the various build options in [CMakeLists](https://github.com/RPCS3/rpcs3/blob/master/CMakeLists.txt).
|
||||
+29
-59
@@ -1,46 +1,19 @@
|
||||
cmake_minimum_required(VERSION 3.8.2)
|
||||
|
||||
project(rpcs3)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8)
|
||||
message(FATAL_ERROR "RPCS3 requires at least gcc-8.")
|
||||
endif()
|
||||
elseif(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
||||
message(FATAL_ERROR "RPCS3 requires at least clang-5.0.")
|
||||
endif()
|
||||
endif()
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
|
||||
option(WITH_GDB "WITH_GDB" OFF)
|
||||
option(USE_NATIVE_INSTRUCTIONS "USE_NATIVE_INSTRUCTIONS makes rpcs3 compile with -march=native, which is useful for local builds, but not good for packages." ON)
|
||||
option(WITH_LLVM "Enable usage of LLVM library" ON)
|
||||
option(BUILD_LLVM_SUBMODULE "Build LLVM from git submodule" ON)
|
||||
option(USE_ALSA "ALSA audio backend" ON)
|
||||
option(USE_PULSE "PulseAudio audio backend" ON)
|
||||
option(USE_LIBEVDEV "libevdev-based joystick support" ON)
|
||||
option(USE_DISCORD_RPC "Discord rich presence integration" ON)
|
||||
option(USE_SYSTEM_ZLIB "Prefer system ZLIB instead of the builtin one" ON)
|
||||
option(USE_VULKAN "Vulkan render backend" ON)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/rpcs3/cmake_modules")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
include(CheckCXXCompilerFlag)
|
||||
option(WITHOUT_LLVM "WITHOUT_LLVM" OFF)
|
||||
|
||||
if (WITH_GDB)
|
||||
add_definitions(-DWITH_GDB_DEBUGGER)
|
||||
endif()
|
||||
|
||||
set(ASMJIT_STATIC TRUE)
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
message(STATUS "No build type selected, default to Release")
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_definitions(-DNDEBUG)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
message( FATAL_ERROR "RPCS3 can only be compiled on 64-bit platforms." )
|
||||
endif()
|
||||
@@ -51,39 +24,36 @@ if (CCACHE_FOUND)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-DUNICODE)
|
||||
add_definitions(-D_WIN32_WINNT=0x0602)
|
||||
add_definitions(-DCMAKE_BUILD)
|
||||
|
||||
# We use libpng's static library and don't need to build the shared library and run the tests
|
||||
set(PNG_SHARED OFF CACHE BOOL "Build shared lib." FORCE)
|
||||
set(PNG_TESTS OFF CACHE BOOL "Build tests." FORCE)
|
||||
|
||||
# Select the version of libpng to use, default is builtin
|
||||
if (NOT USE_SYSTEM_LIBPNG)
|
||||
add_subdirectory( 3rdparty/libpng )
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
include_directories(/opt/local/include)
|
||||
link_directories(/opt/local/lib)
|
||||
endif()
|
||||
|
||||
# Warnings are silenced for 3rdparty code
|
||||
set(CMAKE_CXX_FLAGS -w)
|
||||
set(CMAKE_C_FLAGS -w)
|
||||
set(LLVM_ENABLE_WARNINGS OFF CACHE BOOL "")
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options(/wd4530) # C++ exception handler used, but unwind semantics are not enabled
|
||||
endif()
|
||||
|
||||
add_subdirectory(Vulkan EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(asmjitsrc EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(3rdparty)
|
||||
|
||||
unset(CMAKE_CXX_FLAGS)
|
||||
unset(CMAKE_C_FLAGS)
|
||||
|
||||
if (NOT WIN32)
|
||||
add_compile_options(-pthread)
|
||||
endif()
|
||||
option(VULKAN_PREBUILT "" OFF)
|
||||
|
||||
# TODO: do real installation, including copying directory structure
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_BINARY_DIR}/bin")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_BINARY_DIR}/bin")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_BINARY_DIR}/bin")
|
||||
|
||||
add_subdirectory(rpcs3)
|
||||
add_subdirectory( Vulkan )
|
||||
add_subdirectory( rpcs3 )
|
||||
|
||||
include_directories(3rdparty/hidapi/hidapi)
|
||||
if(APPLE)
|
||||
add_subdirectory(3rdparty/hidapi/mac)
|
||||
#list(APPEND LIBS hidapi)
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
add_subdirectory(3rdparty/hidapi/linux)
|
||||
elseif(MSVC)
|
||||
add_subdirectory(3rdparty/hidapi/windows)
|
||||
else()
|
||||
add_subdirectory(3rdparty/hidapi/libusb)
|
||||
#list(APPEND LIBS hidapi-libusb)
|
||||
endif()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Getting Started
|
||||
|
||||
Before getting started using the emulator, read the [Quickstart Guide](https://rpcs3.net/quickstart) and the [FAQ](https://rpcs3.net/faq). After reading those, if you need support, visit our [Forums](https://forums.rpcs3.net).
|
||||
Before getting started using the emulator, read the [Quickstart Guide](https://rpcs3.net/quickstart) and the [FAQ](https://rpcs3.net/faq). After reading those, if you need support, visit our [Forums](http://www.emunewz.net/forum/forumdisplay.php?fid=172).
|
||||
|
||||
# Issue Reporting
|
||||
|
||||
@@ -13,7 +13,7 @@ Before getting started using the emulator, read the [Quickstart Guide](https://r
|
||||
- Use understandable English. It doesn't need to be perfect, but clear enough to understand your message.
|
||||
- While reporting issues, don't forget to include details about your system (OS, CPU, GPU, etc.), as well as the RPCS3.log file.
|
||||
|
||||
Submitting your test results for Commercial Games must be done on our forums. Please read the [Game Compatibility](https://github.com/RPCS3/rpcs3/wiki/Game-Compatibility) wiki page before doing so.
|
||||
Submitting your test results for Commercial Games must be done on our forums' [Commercial Games](http://www.emunewz.net/forum/forumdisplay.php?fid=193) section. If there's already a thread for the game you want to report post in it, otherwise please make a new thread following the [Guidelines for submitting new games](http://www.emunewz.net/forum/showthread.php?tid=174376).
|
||||
|
||||
# Contributing
|
||||
|
||||
@@ -3,37 +3,83 @@ RPCS3
|
||||
|
||||
[](https://travis-ci.org/RPCS3/rpcs3)
|
||||
[](https://ci.appveyor.com/project/rpcs3/rpcs3/branch/master)
|
||||
[](https://scan.coverity.com/projects/3960)
|
||||
|
||||
The world's first free and open-source PlayStation 3 emulator/debugger, written in C++ for Windows and Linux.
|
||||
The world's first open-source PlayStation 3 emulator/debugger written in C++ for Windows and Linux.
|
||||
|
||||
You can find some basic information on our [**website**](https://rpcs3.net/). Game info is being populated on the [**Wiki**](https://wiki.rpcs3.net/).
|
||||
For discussion about this emulator, PS3 emulation, and game compatibility reports, please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.me/RPCS3).
|
||||
You can find some basic information in our [**website**](https://rpcs3.net/).
|
||||
For discussion about this emulator and PS3 emulation please visit our [**forums**](http://www.emunewz.net/forum/forumdisplay.php?fid=172) and our [**Discord server**](https://discord.me/RPCS3).
|
||||
|
||||
[**Support Lead Developers Nekotekina and kd-11 on Patreon**](https://www.patreon.com/Nekotekina)
|
||||
[**Support Lead Developer Nekotekina on Patreon**](https://www.patreon.com/Nekotekina)
|
||||
|
||||
## Contributing
|
||||
|
||||
If you want to help the project but do not code, the best way to help out is to test games and make bug reports. See:
|
||||
* [Quickstart](https://rpcs3.net/quickstart)
|
||||
## Development
|
||||
|
||||
If you want to contribute as a developer, please take a look at the following pages:
|
||||
If you want to contribute please take a look at the [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style), [Roadmap](https://github.com/RPCS3/rpcs3/wiki/Roadmap) and [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information) pages. You should as well contact any of the developers in the forums or in Discord in order to know more about the current situation of the emulator.
|
||||
|
||||
* [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style)
|
||||
* [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information)
|
||||
* [Roadmap](https://rpcs3.net/roadmap)
|
||||
|
||||
You should also contact any of the developers in the forums or in the Discord server to learn more about the current state of the emulator.
|
||||
## Dependencies
|
||||
|
||||
## Building
|
||||
### Windows
|
||||
* [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+](https://www.cmake.org/download/) (required; add to PATH)
|
||||
* [Python 3.3+](https://www.python.org/downloads/) (required; add to PATH)
|
||||
* [Qt 5.9+](https://www.qt.io/download-open-source/) (required; add QTDIR `<QtInstallFolder>\5.9\msvc2015_64\` environment variable if you do not want to use the Visual Studio Qt Plugin)
|
||||
* [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2015) (optional; see above)
|
||||
|
||||
See [BUILDING.md](BUILDING.md) for more information about how to setup an environment to build RPCS3.
|
||||
### Linux
|
||||
* [Qt 5.9+](https://www.qt.io/download-open-source/)
|
||||
* GCC 5.1+ or Clang 3.5.0+ ([not GCC 6.1](https://github.com/RPCS3/rpcs3/issues/1691))
|
||||
* Debian & Ubuntu: `sudo apt-get install cmake build-essential libasound2-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git qt5-default`
|
||||
* Arch: `sudo pacman -S glew openal cmake llvm qt5-base`
|
||||
* Fedora: `sudo dnf install cmake qt5-devel vulkan-devel libGLEW`
|
||||
|
||||
## Running
|
||||
### Mac OSX
|
||||
Mac OSX is not supported at this moment because it doesn't meet system requirements (OpenGL 4.3)
|
||||
* Xcode 6+ (tested with Xcode 6.4)
|
||||
* Install with Homebrew: `brew install glew`
|
||||
|
||||
Check our friendly [quickstart](https://rpcs3.net/quickstart) guide to make sure your computer meets the minimum system requirements to run RPCS3.
|
||||
|
||||
Don't forget to have your graphics driver up to date and to install the [Visual C++ Redistributable Packages for Visual Studio 2017](https://go.microsoft.com/fwlink/?LinkId=746572) if you are a Windows user.
|
||||
## Building on Windows:
|
||||
To initialize the repository don't forget to execute `git submodule update --init` to pull the submodules.
|
||||
|
||||
### Configuring Qt
|
||||
|
||||
*If you're using Visual Studio 2017 without Qt plugin support (or simply dont want to use it):*
|
||||
1) Add `QTDIR` environment variable and set it to `<QtInstallFolder>\5.9\msvc2017_64\` </br>
|
||||
|
||||
*If you wish to use the Visual Studio plugin for Qt:* </br>
|
||||
1) Go to the Qt5 menu and edit Qt5 options. Add the path to your Qt installation with compiler e.g. `C:\Qt\5.8\msvc2015_64`. </br>
|
||||
2) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
|
||||
|
||||
### Building the projects
|
||||
1) Build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*. </br>
|
||||
2) Press *BUILD* > *Build Solution* or *Rebuild Solution*. </br>
|
||||
|
||||
|
||||
## Building on Linux & Mac OSX:
|
||||
|
||||
1) `git clone https://github.com/RPCS3/rpcs3.git` </br>
|
||||
2) `cd rpcs3/` </br>
|
||||
3) `git submodule update --init` </br>
|
||||
4) `cmake CMakeLists.txt && make GitVersion && make` </br>
|
||||
5) Run RPCS3 with `./bin/rpcs3` </br>
|
||||
|
||||
If you are on OSX and want to build with llvm don't forget to add `-DLLVM_DIR=...` (or wherever llvm brew was installed) to cmake invocation.
|
||||
|
||||
When using GDB, configure it to ignore SIGSEGV signal (`handle SIGSEGV nostop noprint`).
|
||||
|
||||
|
||||
## CMake Build Options (Linux & Mac OSX)
|
||||
|
||||
- ```-DUSE_SYSTEM_LIBPNG=ON/OFF``` (default = *OFF*) </br>
|
||||
Build against the shared libpng instead of using the builtin one. libpng 1.6+ highly recommended. Try this option if you get version conflict errors or only see black game icons.
|
||||
|
||||
- ```-DUSE_SYSTEM_FFMPEG=ON/OFF``` (default = *OFF*) </br>
|
||||
Build against the shared ffmpeg libraries instead of using the builtin patched version. Try this if the builtin version breaks the OpenGL renderer for you.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Most files are licensed under the terms of GNU GPLv2 License; see LICENSE file for details. Some files may be licensed differently; check appropriate file headers for details.
|
||||
Most files are licensed under the terms of GNU GPLv2 License, see LICENSE file for details. Some files may be licensed differently, check appropriate file headers for details.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
#include <memory>
|
||||
#include <cstddef>
|
||||
|
||||
// Unfinished. Only std::default_delete will work as expected.
|
||||
template<typename T, typename D>
|
||||
|
||||
+49
-30
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include <cstring>
|
||||
|
||||
// 128-bit vector type and also se_storage<> storage type
|
||||
union alignas(16) v128
|
||||
@@ -308,7 +307,7 @@ struct offset32_array<v128::masked_array_t<T, N, M>>
|
||||
template <typename Arg>
|
||||
static inline u32 index32(const Arg& arg)
|
||||
{
|
||||
return u32{sizeof(T)} * (static_cast<u32>(arg) ^ static_cast<u32>(M));
|
||||
return SIZE_32(T) * (static_cast<u32>(arg) ^ static_cast<u32>(M));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -335,10 +334,7 @@ inline v128 operator~(const v128& other)
|
||||
template <typename T, std::size_t Align, std::size_t Size>
|
||||
struct se_storage
|
||||
{
|
||||
struct type
|
||||
{
|
||||
alignas(Align) std::byte data[Size];
|
||||
};
|
||||
using type = std::aligned_storage_t<Size, Align>;
|
||||
|
||||
// Unoptimized generic byteswap for unaligned data
|
||||
static void reverse(u8* dst, const u8* src)
|
||||
@@ -362,6 +358,16 @@ struct se_storage
|
||||
reverse(reinterpret_cast<u8*>(&result), reinterpret_cast<const u8*>(&src));
|
||||
return result;
|
||||
}
|
||||
|
||||
static type copy(const type& src)
|
||||
{
|
||||
type result;
|
||||
for (std::size_t i = 0; i < Size; i++)
|
||||
{
|
||||
reinterpret_cast<u8*>(&result)[i] = reinterpret_cast<const u8*>(&src)[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -380,12 +386,18 @@ struct se_storage<T, 2, 2>
|
||||
|
||||
static inline u16 to(const T& src)
|
||||
{
|
||||
return swap(std::bit_cast<u16>(src));
|
||||
return swap(reinterpret_cast<const u16&>(src));
|
||||
}
|
||||
|
||||
static inline T from(u16 src)
|
||||
{
|
||||
return std::bit_cast<T, u16>(swap(src));
|
||||
const u16 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -405,12 +417,18 @@ struct se_storage<T, 4, 4>
|
||||
|
||||
static inline u32 to(const T& src)
|
||||
{
|
||||
return swap(std::bit_cast<u32>(src));
|
||||
return swap(reinterpret_cast<const u32&>(src));
|
||||
}
|
||||
|
||||
static inline T from(u32 src)
|
||||
{
|
||||
return std::bit_cast<T, u32>(swap(src));
|
||||
const u32 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -430,12 +448,18 @@ struct se_storage<T, 8, 8>
|
||||
|
||||
static inline u64 to(const T& src)
|
||||
{
|
||||
return swap(std::bit_cast<u64>(src));
|
||||
return swap(reinterpret_cast<const u64&>(src));
|
||||
}
|
||||
|
||||
static inline T from(u64 src)
|
||||
{
|
||||
return std::bit_cast<T, u64>(swap(src));
|
||||
const u64 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -446,17 +470,23 @@ struct se_storage<T, 16, 16>
|
||||
|
||||
static inline v128 swap(const v128& src)
|
||||
{
|
||||
return v128::from64(se_storage<u64>::swap(src._u64[1]), se_storage<u64>::swap(src._u64[0]));
|
||||
return v128::fromV(_mm_shuffle_epi8(src.vi, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)));
|
||||
}
|
||||
|
||||
static inline v128 to(const T& src)
|
||||
{
|
||||
return swap(std::bit_cast<v128>(src));
|
||||
return swap(reinterpret_cast<const v128&>(src));
|
||||
}
|
||||
|
||||
static inline T from(const v128& src)
|
||||
{
|
||||
return std::bit_cast<T, v128>(swap(src));
|
||||
const v128 result = swap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
|
||||
static inline T copy(const T& src)
|
||||
{
|
||||
return src;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -490,11 +520,6 @@ public:
|
||||
return storage::from(m_data);
|
||||
}
|
||||
|
||||
stype& raw()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
se_t& operator=(const se_t&) = default;
|
||||
|
||||
se_t& operator=(type value)
|
||||
@@ -529,33 +554,27 @@ public:
|
||||
se_t() = default;
|
||||
|
||||
se_t(type value)
|
||||
: m_data(std::bit_cast<stype>(value))
|
||||
: m_data(reinterpret_cast<const stype&>(value))
|
||||
{
|
||||
}
|
||||
|
||||
type value() const
|
||||
{
|
||||
return std::bit_cast<type>(m_data);
|
||||
}
|
||||
|
||||
stype& raw()
|
||||
{
|
||||
return m_data;
|
||||
return storage::copy(reinterpret_cast<const type&>(m_data));
|
||||
}
|
||||
|
||||
se_t& operator=(const se_t& value) = default;
|
||||
|
||||
se_t& operator=(type value)
|
||||
{
|
||||
m_data = std::bit_cast<stype>(value);
|
||||
return *this;
|
||||
return m_data = reinterpret_cast<const stype&>(value), *this;
|
||||
}
|
||||
|
||||
using simple_type = simple_t<T>;
|
||||
|
||||
operator type() const
|
||||
{
|
||||
return value();
|
||||
return storage::copy(reinterpret_cast<const type&>(m_data));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include <limits>
|
||||
|
||||
template<typename T, uint N>
|
||||
struct bf_base
|
||||
@@ -16,11 +15,11 @@ struct bf_base
|
||||
// Field bitsize
|
||||
static constexpr uint bitsize = N;
|
||||
|
||||
// All ones mask
|
||||
static constexpr utype mask1 = std::numeric_limits<utype>::max();
|
||||
|
||||
// Value mask
|
||||
static constexpr utype vmask = mask1 >> (bitmax - bitsize);
|
||||
static constexpr utype vmask = static_cast<utype>(~utype{} >> (bitmax - bitsize));
|
||||
|
||||
// All ones mask
|
||||
static constexpr utype mask1 = static_cast<utype>(~utype{});
|
||||
|
||||
protected:
|
||||
type m_data;
|
||||
|
||||
@@ -1,243 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Utilities/types.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "windows.h"
|
||||
#include "tlhelp32.h"
|
||||
#else
|
||||
#include "stdlib.h"
|
||||
#include "sys/times.h"
|
||||
#include "sys/types.h"
|
||||
#include "unistd.h"
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <mach/mach_init.h>
|
||||
# include <mach/task.h>
|
||||
# include <mach/vm_map.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
# include <dirent.h>
|
||||
#endif
|
||||
|
||||
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
# include <sys/sysctl.h>
|
||||
# if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
# include <sys/user.h>
|
||||
# endif
|
||||
|
||||
# if defined(__NetBSD__)
|
||||
# undef KERN_PROC
|
||||
# define KERN_PROC KERN_PROC2
|
||||
# define kinfo_proc kinfo_proc2
|
||||
# endif
|
||||
|
||||
# if defined(__DragonFly__)
|
||||
# define KP_NLWP(kp) (kp.kp_nthreads)
|
||||
# elif defined(__FreeBSD__)
|
||||
# define KP_NLWP(kp) (kp.ki_numthreads)
|
||||
# elif defined(__NetBSD__)
|
||||
# define KP_NLWP(kp) (kp.p_nlwps)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
class CPUStats
|
||||
{
|
||||
#ifdef _WIN32
|
||||
HANDLE m_self;
|
||||
using time_type = ULARGE_INTEGER;
|
||||
#else
|
||||
using time_type = clock_t;
|
||||
#endif
|
||||
|
||||
private:
|
||||
s32 m_num_processors;
|
||||
time_type m_last_cpu, m_sys_cpu, m_usr_cpu;
|
||||
|
||||
public:
|
||||
CPUStats()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO sysInfo;
|
||||
FILETIME ftime, fsys, fuser;
|
||||
|
||||
GetSystemInfo(&sysInfo);
|
||||
m_num_processors = sysInfo.dwNumberOfProcessors;
|
||||
|
||||
GetSystemTimeAsFileTime(&ftime);
|
||||
memcpy(&m_last_cpu, &ftime, sizeof(FILETIME));
|
||||
|
||||
m_self = GetCurrentProcess();
|
||||
GetProcessTimes(m_self, &ftime, &ftime, &fsys, &fuser);
|
||||
memcpy(&m_sys_cpu, &fsys, sizeof(FILETIME));
|
||||
memcpy(&m_usr_cpu, &fuser, sizeof(FILETIME));
|
||||
#else
|
||||
struct tms timeSample;
|
||||
|
||||
m_last_cpu = times(&timeSample);
|
||||
m_sys_cpu = timeSample.tms_stime;
|
||||
m_usr_cpu = timeSample.tms_utime;
|
||||
m_num_processors = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
#endif
|
||||
}
|
||||
|
||||
double get_usage()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
FILETIME ftime, fsys, fusr;
|
||||
ULARGE_INTEGER now, sys, usr;
|
||||
|
||||
GetSystemTimeAsFileTime(&ftime);
|
||||
memcpy(&now, &ftime, sizeof(FILETIME));
|
||||
|
||||
GetProcessTimes(m_self, &ftime, &ftime, &fsys, &fusr);
|
||||
memcpy(&sys, &fsys, sizeof(FILETIME));
|
||||
memcpy(&usr, &fusr, sizeof(FILETIME));
|
||||
double percent = double(sys.QuadPart - m_sys_cpu.QuadPart) + (usr.QuadPart - m_usr_cpu.QuadPart);
|
||||
percent /= (now.QuadPart - m_last_cpu.QuadPart);
|
||||
percent /= m_num_processors;
|
||||
|
||||
m_last_cpu = now;
|
||||
m_usr_cpu = usr;
|
||||
m_sys_cpu = sys;
|
||||
|
||||
return std::clamp(percent * 100, 0.0, 100.0);
|
||||
#else
|
||||
struct tms timeSample;
|
||||
clock_t now;
|
||||
double percent;
|
||||
|
||||
now = times(&timeSample);
|
||||
if (now <= m_last_cpu || timeSample.tms_stime < m_sys_cpu || timeSample.tms_utime < m_usr_cpu)
|
||||
{
|
||||
// Overflow detection. Just skip this value.
|
||||
percent = -1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
percent = (timeSample.tms_stime - m_sys_cpu) + (timeSample.tms_utime - m_usr_cpu);
|
||||
percent /= (now - m_last_cpu);
|
||||
percent /= m_num_processors;
|
||||
percent *= 100;
|
||||
}
|
||||
m_last_cpu = now;
|
||||
m_sys_cpu = timeSample.tms_stime;
|
||||
m_usr_cpu = timeSample.tms_utime;
|
||||
|
||||
return percent;
|
||||
#endif
|
||||
}
|
||||
|
||||
static u32 get_thread_count()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// first determine the id of the current process
|
||||
DWORD const id = GetCurrentProcessId();
|
||||
|
||||
// then get a process list snapshot.
|
||||
HANDLE const snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
|
||||
|
||||
// initialize the process entry structure.
|
||||
PROCESSENTRY32 entry = {0};
|
||||
entry.dwSize = sizeof(entry);
|
||||
|
||||
// get the first process info.
|
||||
BOOL ret = true;
|
||||
ret = Process32First(snapshot, &entry);
|
||||
while (ret && entry.th32ProcessID != id)
|
||||
{
|
||||
ret = Process32Next(snapshot, &entry);
|
||||
}
|
||||
CloseHandle(snapshot);
|
||||
return ret ? entry.cntThreads : 0;
|
||||
#elif defined(__APPLE__)
|
||||
const task_t task = mach_task_self();
|
||||
mach_msg_type_number_t thread_count;
|
||||
thread_act_array_t thread_list;
|
||||
if (task_threads(task, &thread_list, &thread_count) != KERN_SUCCESS)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
vm_deallocate(task, reinterpret_cast<vm_address_t>(thread_list),
|
||||
sizeof(thread_t) * thread_count);
|
||||
return static_cast<u32>(thread_count);
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
int mib[] = {
|
||||
CTL_KERN,
|
||||
KERN_PROC,
|
||||
KERN_PROC_PID,
|
||||
getpid(),
|
||||
#if defined(__NetBSD__)
|
||||
sizeof(struct kinfo_proc),
|
||||
1,
|
||||
#endif
|
||||
};
|
||||
u_int miblen = std::size(mib);
|
||||
struct kinfo_proc info;
|
||||
size_t size = sizeof(info);
|
||||
if (sysctl(mib, miblen, &info, &size, NULL, 0))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return KP_NLWP(info);
|
||||
#elif defined(__OpenBSD__)
|
||||
int mib[] = {
|
||||
CTL_KERN,
|
||||
KERN_PROC,
|
||||
KERN_PROC_PID | KERN_PROC_SHOW_THREADS,
|
||||
getpid(),
|
||||
sizeof(struct kinfo_proc),
|
||||
0,
|
||||
};
|
||||
u_int miblen = std::size(mib);
|
||||
|
||||
// get number of structs
|
||||
size_t size;
|
||||
if (sysctl(mib, miblen, NULL, &size, NULL, 0))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
mib[5] = size / mib[4];
|
||||
|
||||
// populate array of structs
|
||||
struct kinfo_proc info[mib[5]];
|
||||
if (sysctl(mib, miblen, &info, &size, NULL, 0))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// exclude empty members
|
||||
u32 thread_count{0};
|
||||
for (int i = 0; i < size / mib[4]; i++)
|
||||
{
|
||||
if (info[i].p_tid != -1)
|
||||
++thread_count;
|
||||
}
|
||||
return thread_count;
|
||||
#elif defined(__linux__)
|
||||
u32 thread_count{0};
|
||||
|
||||
DIR* proc_dir = opendir("/proc/self/task");
|
||||
if (proc_dir)
|
||||
{
|
||||
// proc available, iterate through tasks and count them
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(proc_dir)) != NULL)
|
||||
{
|
||||
if (entry->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
++thread_count;
|
||||
}
|
||||
|
||||
closedir(proc_dir);
|
||||
}
|
||||
return thread_count;
|
||||
#else
|
||||
// unimplemented
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
+10
-35
@@ -3,10 +3,10 @@
|
||||
|
||||
#include "yaml-cpp/yaml.h"
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
namespace cfg
|
||||
{
|
||||
logs::channel cfg("CFG");
|
||||
|
||||
_base::_base(type _type)
|
||||
: m_type(_type)
|
||||
{
|
||||
@@ -48,11 +48,6 @@ namespace cfg
|
||||
static void decode(const YAML::Node& data, class _base& rhs);
|
||||
}
|
||||
|
||||
std::vector<std::string> cfg::make_int_range(s64 min, s64 max)
|
||||
{
|
||||
return {std::to_string(min), std::to_string(max)};
|
||||
}
|
||||
|
||||
bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
|
||||
{
|
||||
// TODO: this could be rewritten without exceptions (but it should be as safe as possible and provide logs)
|
||||
@@ -65,19 +60,19 @@ bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_int('%s'): exception: %s", value, e.what());
|
||||
if (out) cfg.error("cfg::try_to_int('%s'): exception: %s", value, e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pos != value.size())
|
||||
{
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_int('%s'): unexpected characters (pos=%zu)", value, pos);
|
||||
if (out) cfg.error("cfg::try_to_int('%s'): unexpected characters (pos=%zu)", value, pos);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result < min || result > max)
|
||||
{
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_int('%s'): out of bounds (%lld..%lld)", value, min, max);
|
||||
if (out) cfg.error("cfg::try_to_int('%s'): out of bounds (%lld..%lld)", value, min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -87,8 +82,6 @@ bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
|
||||
|
||||
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, const std::string& value)
|
||||
{
|
||||
u64 max = -1;
|
||||
|
||||
for (u64 i = 0;; i++)
|
||||
{
|
||||
std::string var;
|
||||
@@ -106,8 +99,6 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
max = i;
|
||||
}
|
||||
|
||||
try
|
||||
@@ -117,22 +108,14 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
|
||||
|
||||
if (pos != value.size())
|
||||
{
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_enum_value('%s'): unexpected characters (pos=%zu)", value, pos);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (val > max)
|
||||
{
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_enum_value('%s'): out of bounds(0..%u)", value, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out) *out = val;
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
catch (...)
|
||||
{
|
||||
if (out) LOG_ERROR(GENERAL, "cfg::try_to_enum_value('%s'): invalid enum value: %s", value, e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -200,12 +183,9 @@ void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs)
|
||||
out << YAML::EndMap;
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
out << rhs.to_string();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
out << rhs.to_string();
|
||||
}
|
||||
|
||||
void cfg::decode(const YAML::Node& data, cfg::_base& rhs)
|
||||
@@ -288,19 +268,14 @@ std::string cfg::node::to_string() const
|
||||
YAML::Emitter out;
|
||||
cfg::encode(out, *this);
|
||||
|
||||
return {out.c_str(), out.size()};
|
||||
return{ out.c_str(), out.size() };
|
||||
}
|
||||
|
||||
bool cfg::node::from_string(const std::string& value) try
|
||||
bool cfg::node::from_string(const std::string& value)
|
||||
{
|
||||
cfg::decode(YAML::Load(value), *this);
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_FATAL(GENERAL, "%s thrown: %s", typeid(e).name(), e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
void cfg::node::from_default()
|
||||
{
|
||||
|
||||
+7
-45
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/types.h"
|
||||
#include "Utilities/StrFmt.h"
|
||||
@@ -12,9 +12,6 @@
|
||||
|
||||
namespace cfg
|
||||
{
|
||||
// Format min and max values
|
||||
std::vector<std::string> make_int_range(s64 min, s64 max);
|
||||
|
||||
// Convert string to signed integer
|
||||
bool try_to_int64(s64* out, const std::string& value, s64 min, s64 max);
|
||||
|
||||
@@ -49,10 +46,9 @@ namespace cfg
|
||||
_base(type _type, class node* owner, const std::string& name);
|
||||
|
||||
public:
|
||||
// Disallow copy/move constructors and assignments
|
||||
_base(const _base&) = delete;
|
||||
|
||||
_base& operator=(const _base&) = delete;
|
||||
|
||||
// Get type
|
||||
type get_type() const { return m_type; }
|
||||
|
||||
@@ -119,7 +115,7 @@ namespace cfg
|
||||
bool m_value;
|
||||
|
||||
public:
|
||||
bool def;
|
||||
const bool def;
|
||||
|
||||
_bool(node* owner, const std::string& name, bool def = false)
|
||||
: _base(type::_bool, owner, name)
|
||||
@@ -133,11 +129,6 @@ namespace cfg
|
||||
return m_value;
|
||||
}
|
||||
|
||||
const bool& get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void from_default() override;
|
||||
|
||||
std::string to_string() const override
|
||||
@@ -156,11 +147,6 @@ namespace cfg
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void set(const bool& value)
|
||||
{
|
||||
m_value = value;
|
||||
}
|
||||
};
|
||||
|
||||
// Value node with fixed set of possible values, each maps to an enum value of type T.
|
||||
@@ -184,11 +170,6 @@ namespace cfg
|
||||
return m_value;
|
||||
}
|
||||
|
||||
const T& get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
m_value = def;
|
||||
@@ -233,11 +214,7 @@ namespace cfg
|
||||
int_type m_value;
|
||||
|
||||
public:
|
||||
int_type def;
|
||||
|
||||
// Expose range
|
||||
static const s64 max = Max;
|
||||
static const s64 min = Min;
|
||||
const int_type def;
|
||||
|
||||
_int(node* owner, const std::string& name, int_type def = std::min<int_type>(Max, std::max<int_type>(Min, 0)))
|
||||
: _base(type::_int, owner, name)
|
||||
@@ -251,11 +228,6 @@ namespace cfg
|
||||
return m_value;
|
||||
}
|
||||
|
||||
const int_type& get() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
m_value = def;
|
||||
@@ -277,16 +249,6 @@ namespace cfg
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void set(const s64& value)
|
||||
{
|
||||
m_value = static_cast<int_type>(value);
|
||||
}
|
||||
|
||||
std::vector<std::string> to_list() const override
|
||||
{
|
||||
return make_int_range(Min, Max);
|
||||
}
|
||||
};
|
||||
|
||||
// Alias for 32 bit int
|
||||
@@ -301,7 +263,7 @@ namespace cfg
|
||||
std::string m_value;
|
||||
|
||||
public:
|
||||
std::string def;
|
||||
const std::string def;
|
||||
|
||||
string(node* owner, const std::string& name, const std::string& def = {})
|
||||
: _base(type::string, owner, name)
|
||||
@@ -351,7 +313,7 @@ namespace cfg
|
||||
{
|
||||
}
|
||||
|
||||
const std::set<std::string>& get_set() const
|
||||
std::set<std::string> get_set() const
|
||||
{
|
||||
return m_set;
|
||||
}
|
||||
@@ -386,7 +348,7 @@ namespace cfg
|
||||
{
|
||||
}
|
||||
|
||||
const std::map<std::string, logs::level>& get_map() const
|
||||
std::map<std::string, logs::level> get_map() const
|
||||
{
|
||||
return m_map;
|
||||
}
|
||||
|
||||
+139
-492
@@ -8,8 +8,6 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <typeinfo>
|
||||
#include <map>
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
@@ -76,25 +74,10 @@ static time_t to_time(const FILETIME& ft)
|
||||
|
||||
static FILETIME from_time(s64 _time)
|
||||
{
|
||||
const ullong wtime = (_time + 11644473600ULL) * 10000000ULL;
|
||||
FILETIME result;
|
||||
|
||||
if (_time <= -11644473600ll)
|
||||
{
|
||||
result.dwLowDateTime = 0;
|
||||
result.dwHighDateTime = 0;
|
||||
}
|
||||
else if (_time > INT64_MAX / 10000000ll - 11644473600ll)
|
||||
{
|
||||
result.dwLowDateTime = 0xffffffff;
|
||||
result.dwHighDateTime = 0x7fffffff;
|
||||
}
|
||||
else
|
||||
{
|
||||
const ullong wtime = (_time + 11644473600ull) * 10000000ull;
|
||||
result.dwLowDateTime = static_cast<DWORD>(wtime);
|
||||
result.dwHighDateTime = static_cast<DWORD>(wtime >> 32);
|
||||
}
|
||||
|
||||
result.dwLowDateTime = static_cast<DWORD>(wtime);
|
||||
result.dwHighDateTime = static_cast<DWORD>(wtime >> 32);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -110,10 +93,6 @@ static fs::error to_error(DWORD e)
|
||||
case ERROR_NEGATIVE_SEEK: return fs::error::inval;
|
||||
case ERROR_DIRECTORY: return fs::error::inval;
|
||||
case ERROR_INVALID_NAME: return fs::error::inval;
|
||||
case ERROR_SHARING_VIOLATION: return fs::error::acces;
|
||||
case ERROR_DIR_NOT_EMPTY: return fs::error::notempty;
|
||||
case ERROR_NOT_READY: return fs::error::noent;
|
||||
//case ERROR_INVALID_PARAMETER: return fs::error::inval;
|
||||
default: fmt::throw_exception("Unknown Win32 error: %u.", e);
|
||||
}
|
||||
}
|
||||
@@ -124,8 +103,6 @@ static fs::error to_error(DWORD e)
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/uio.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
@@ -136,12 +113,10 @@ static fs::error to_error(DWORD e)
|
||||
#if defined(__APPLE__)
|
||||
#include <copyfile.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
#include <sys/socket.h> // sendfile
|
||||
#elif defined(__linux__) || defined(__sun)
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/fs.h>
|
||||
#else
|
||||
#include <fstream>
|
||||
#endif
|
||||
|
||||
static fs::error to_error(int e)
|
||||
@@ -152,7 +127,6 @@ static fs::error to_error(int e)
|
||||
case EEXIST: return fs::error::exist;
|
||||
case EINVAL: return fs::error::inval;
|
||||
case EACCES: return fs::error::acces;
|
||||
case ENOTEMPTY: return fs::error::notempty;
|
||||
default: fmt::throw_exception("Unknown system error: %d.", e);
|
||||
}
|
||||
}
|
||||
@@ -195,43 +169,6 @@ namespace fs
|
||||
// Do notning
|
||||
}
|
||||
|
||||
fs::native_handle fs::file_base::get_handle()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
u64 file_base::write_gather(const iovec_clone* buffers, u64 buf_count)
|
||||
{
|
||||
u64 total = 0;
|
||||
|
||||
for (u64 i = 0; i < buf_count; i++)
|
||||
{
|
||||
if (!buffers[i].iov_base || buffers[i].iov_len + total < total)
|
||||
{
|
||||
g_tls_error = error::inval;
|
||||
return -1;
|
||||
}
|
||||
|
||||
total += buffers[i].iov_len;
|
||||
}
|
||||
|
||||
const auto buf = std::make_unique<uchar[]>(total);
|
||||
|
||||
u64 copied = 0;
|
||||
|
||||
for (u64 i = 0; i < buf_count; i++)
|
||||
{
|
||||
std::memcpy(buf.get() + copied, buffers[i].iov_base, buffers[i].iov_len);
|
||||
copied += buffers[i].iov_len;
|
||||
}
|
||||
|
||||
return this->write(buf.get(), total);
|
||||
}
|
||||
|
||||
dir_base::~dir_base()
|
||||
{
|
||||
}
|
||||
@@ -257,7 +194,7 @@ std::shared_ptr<fs::device_base> fs::device_manager::get_device(const std::strin
|
||||
|
||||
std::shared_ptr<fs::device_base> fs::device_manager::set_device(const std::string& name, const std::shared_ptr<device_base>& device)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
writer_lock lock(m_mutex);
|
||||
|
||||
return m_map[name] = device;
|
||||
}
|
||||
@@ -324,7 +261,7 @@ bool fs::stat(const std::string& path, stat_t& info)
|
||||
info.size = (u64)attrs.nFileSizeLow | ((u64)attrs.nFileSizeHigh << 32);
|
||||
info.atime = to_time(attrs.ftLastAccessTime);
|
||||
info.mtime = to_time(attrs.ftLastWriteTime);
|
||||
info.ctime = info.mtime;
|
||||
info.ctime = to_time(attrs.ftCreationTime);
|
||||
#else
|
||||
struct ::stat file_info;
|
||||
if (::stat(path.c_str(), &file_info) != 0)
|
||||
@@ -338,12 +275,9 @@ bool fs::stat(const std::string& path, stat_t& info)
|
||||
info.size = file_info.st_size;
|
||||
info.atime = file_info.st_atime;
|
||||
info.mtime = file_info.st_mtime;
|
||||
info.ctime = info.mtime;
|
||||
info.ctime = file_info.st_ctime;
|
||||
#endif
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -539,30 +473,18 @@ bool fs::create_dir(const std::string& path)
|
||||
|
||||
bool fs::create_path(const std::string& path)
|
||||
{
|
||||
const std::string parent = get_parent_dir(path);
|
||||
const auto& parent = get_parent_dir(path);
|
||||
|
||||
if (!parent.empty() && !create_path(parent))
|
||||
if (!parent.empty() && !is_dir(parent) && !create_path(parent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!create_dir(path) && g_tls_error != error::exist)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return create_dir(path);
|
||||
}
|
||||
|
||||
bool fs::remove_dir(const std::string& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// Don't allow removing empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
return device->remove_dir(path);
|
||||
@@ -587,15 +509,8 @@ bool fs::remove_dir(const std::string& path)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool fs::rename(const std::string& from, const std::string& to, bool overwrite)
|
||||
bool fs::rename(const std::string& from, const std::string& to)
|
||||
{
|
||||
if (from.empty() || to.empty())
|
||||
{
|
||||
// Don't allow opening empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto device = get_virtual_device(from);
|
||||
|
||||
if (device != get_virtual_device(to))
|
||||
@@ -609,58 +524,14 @@ bool fs::rename(const std::string& from, const std::string& to, bool overwrite)
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
const auto ws1 = to_wchar(from);
|
||||
const auto ws2 = to_wchar(to);
|
||||
|
||||
if (!MoveFileExW(ws1.get(), ws2.get(), overwrite ? MOVEFILE_REPLACE_EXISTING : 0))
|
||||
if (!MoveFileW(to_wchar(from).get(), to_wchar(to).get()))
|
||||
{
|
||||
DWORD error1 = GetLastError();
|
||||
|
||||
if (overwrite && error1 == ERROR_ACCESS_DENIED && is_dir(from) && is_dir(to))
|
||||
{
|
||||
if (RemoveDirectoryW(ws2.get()))
|
||||
{
|
||||
if (MoveFileW(ws1.get(), ws2.get()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
error1 = GetLastError();
|
||||
CreateDirectoryW(ws2.get(), NULL); // TODO
|
||||
}
|
||||
else
|
||||
{
|
||||
error1 = GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
g_tls_error = to_error(error1);
|
||||
g_tls_error = to_error(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
|
||||
#ifdef __linux__
|
||||
if (syscall(SYS_renameat2, AT_FDCWD, from.c_str(), AT_FDCWD, to.c_str(), overwrite ? 0 : 1 /* RENAME_NOREPLACE */) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the filesystem doesn't support RENAME_NOREPLACE, it returns EINVAL. Retry with fallback method in that case.
|
||||
if (errno != EINVAL || overwrite)
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!overwrite && exists(to))
|
||||
{
|
||||
g_tls_error = fs::error::exist;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (::rename(from.c_str(), to.c_str()) != 0)
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
@@ -688,7 +559,7 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
|
||||
}
|
||||
|
||||
return true;
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined(__sun)
|
||||
#else
|
||||
/* Source: http://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c */
|
||||
|
||||
const int input = ::open(from.c_str(), O_RDONLY);
|
||||
@@ -712,13 +583,17 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
|
||||
#if defined(__APPLE__)
|
||||
// fcopyfile works on OS X 10.5+
|
||||
if (::fcopyfile(input, output, 0, COPYFILE_ALL))
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
if (::sendfile(input, output, 0, 0, NULL, NULL, 0))
|
||||
#elif defined(__linux__) || defined(__sun)
|
||||
// sendfile will work with non-socket output (i.e. regular file) on Linux 2.6.33+
|
||||
off_t bytes_copied = 0;
|
||||
struct ::stat fileinfo = { 0 };
|
||||
if (::fstat(input, &fileinfo) || ::sendfile(output, input, &bytes_copied, fileinfo.st_size))
|
||||
#else
|
||||
#error "Native file copy implementation is missing"
|
||||
#else // NetBSD, OpenBSD, etc.
|
||||
fmt::throw_exception("fs::copy_file() isn't implemented for this platform.\nFrom: %s\nTo: %s", from, to);
|
||||
errno = 0;
|
||||
if (true)
|
||||
#endif
|
||||
{
|
||||
const int err = errno;
|
||||
@@ -731,31 +606,6 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
|
||||
|
||||
::close(input);
|
||||
::close(output);
|
||||
return true;
|
||||
#else // fallback
|
||||
{
|
||||
std::ifstream out{to, std::ios::binary};
|
||||
if (out.good() && !overwrite)
|
||||
{
|
||||
g_tls_error = to_error(EEXIST);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::ifstream in{from, std::ios::binary};
|
||||
std::ofstream out{to, std::ios::binary};
|
||||
|
||||
if (!in.good() || !out.good())
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::istreambuf_iterator<char> bin(in);
|
||||
std::istreambuf_iterator<char> ein;
|
||||
std::ostreambuf_iterator<char> bout(out);
|
||||
std::copy(bin, ein, bout);
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
@@ -795,17 +645,18 @@ bool fs::truncate_file(const std::string& path, u64 length)
|
||||
|
||||
#ifdef _WIN32
|
||||
// Open the file
|
||||
const auto handle = CreateFileW(to_wchar(path).get(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
const auto handle = CreateFileW(to_wchar(path).get(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
g_tls_error = to_error(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE_END_OF_FILE_INFO _eof;
|
||||
_eof.EndOfFile.QuadPart = length;
|
||||
LARGE_INTEGER distance;
|
||||
distance.QuadPart = length;
|
||||
|
||||
if (!SetFileInformationByHandle(handle, FileEndOfFileInfo, &_eof, sizeof(_eof)))
|
||||
// Seek and truncate
|
||||
if (!SetFilePointerEx(handle, distance, NULL, FILE_BEGIN) || !SetEndOfFile(handle))
|
||||
{
|
||||
g_tls_error = to_error(GetLastError());
|
||||
CloseHandle(handle);
|
||||
@@ -834,7 +685,7 @@ bool fs::utime(const std::string& path, s64 atime, s64 mtime)
|
||||
|
||||
#ifdef _WIN32
|
||||
// Open the file
|
||||
const auto handle = CreateFileW(to_wchar(path).get(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
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());
|
||||
@@ -879,13 +730,6 @@ void fs::file::xfail() const
|
||||
|
||||
fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// Don't allow opening empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
if (auto&& _file = device->open(path, mode))
|
||||
@@ -899,39 +743,28 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD access = 0;
|
||||
if (mode & fs::read) access |= GENERIC_READ;
|
||||
if (mode & fs::write) access |= DELETE | (mode & fs::append ? FILE_APPEND_DATA : GENERIC_WRITE);
|
||||
if (test(mode & fs::read)) access |= GENERIC_READ;
|
||||
if (test(mode & fs::write)) access |= test(mode & fs::append) ? FILE_APPEND_DATA : GENERIC_WRITE;
|
||||
|
||||
DWORD disp = 0;
|
||||
if (mode & fs::create)
|
||||
if (test(mode & fs::create))
|
||||
{
|
||||
disp =
|
||||
mode & fs::excl ? CREATE_NEW :
|
||||
mode & fs::trunc ? CREATE_ALWAYS : OPEN_ALWAYS;
|
||||
test(mode & fs::excl) ? CREATE_NEW :
|
||||
test(mode & fs::trunc) ? CREATE_ALWAYS : OPEN_ALWAYS;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode & fs::excl)
|
||||
if (test(mode & fs::excl))
|
||||
{
|
||||
g_tls_error = error::inval;
|
||||
return;
|
||||
}
|
||||
|
||||
disp = mode & fs::trunc ? TRUNCATE_EXISTING : OPEN_EXISTING;
|
||||
disp = test(mode & fs::trunc) ? TRUNCATE_EXISTING : OPEN_EXISTING;
|
||||
}
|
||||
|
||||
DWORD share = 0;
|
||||
if (!(mode & fs::unread) || !(mode & fs::write))
|
||||
{
|
||||
share |= FILE_SHARE_READ;
|
||||
}
|
||||
|
||||
if (!(mode & (fs::lock + fs::unread)) || !(mode & fs::write))
|
||||
{
|
||||
share |= FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
||||
}
|
||||
|
||||
const HANDLE handle = CreateFileW(to_wchar(path).get(), access, share, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
const HANDLE handle = CreateFileW(to_wchar(path).get(), access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
@@ -965,10 +798,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
info.size = this->size();
|
||||
info.atime = to_time(basic_info.LastAccessTime);
|
||||
info.mtime = to_time(basic_info.ChangeTime);
|
||||
info.ctime = info.mtime;
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
info.ctime = to_time(basic_info.CreationTime);
|
||||
|
||||
return info;
|
||||
}
|
||||
@@ -980,15 +810,23 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
|
||||
bool trunc(u64 length) override
|
||||
{
|
||||
FILE_END_OF_FILE_INFO _eof;
|
||||
_eof.EndOfFile.QuadPart = length;
|
||||
LARGE_INTEGER old, pos;
|
||||
|
||||
if (!SetFileInformationByHandle(m_handle, FileEndOfFileInfo, &_eof, sizeof(_eof)))
|
||||
pos.QuadPart = 0;
|
||||
if (!SetFilePointerEx(m_handle, pos, &old, FILE_CURRENT)) // get old position
|
||||
{
|
||||
g_tls_error = to_error(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
pos.QuadPart = length;
|
||||
if (!SetFilePointerEx(m_handle, pos, NULL, FILE_BEGIN)) // set new position
|
||||
{
|
||||
g_tls_error = to_error(GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
verify("file::trunc" HERE), SetEndOfFile(m_handle), SetFilePointerEx(m_handle, old, NULL, FILE_BEGIN);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1041,34 +879,22 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
|
||||
return size.QuadPart;
|
||||
}
|
||||
|
||||
native_handle get_handle() override
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
};
|
||||
|
||||
m_file = std::make_unique<windows_file>(handle);
|
||||
#else
|
||||
int flags = 0;
|
||||
|
||||
if (mode & fs::read && mode & fs::write) flags |= O_RDWR;
|
||||
else if (mode & fs::read) flags |= O_RDONLY;
|
||||
else if (mode & fs::write) flags |= O_WRONLY;
|
||||
if (test(mode & fs::read) && test(mode & fs::write)) flags |= O_RDWR;
|
||||
else if (test(mode & fs::read)) flags |= O_RDONLY;
|
||||
else if (test(mode & fs::write)) flags |= O_WRONLY;
|
||||
|
||||
if (mode & fs::append) flags |= O_APPEND;
|
||||
if (mode & fs::create) flags |= O_CREAT;
|
||||
if (mode & fs::trunc && !(mode & (fs::lock + fs::unread))) flags |= O_TRUNC;
|
||||
if (mode & fs::excl) flags |= O_EXCL;
|
||||
if (test(mode & fs::append)) flags |= O_APPEND;
|
||||
if (test(mode & fs::create)) flags |= O_CREAT;
|
||||
if (test(mode & fs::trunc)) flags |= O_TRUNC;
|
||||
if (test(mode & fs::excl)) flags |= O_EXCL;
|
||||
|
||||
int perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
|
||||
if (mode & fs::write && mode & fs::unread)
|
||||
{
|
||||
perm = 0;
|
||||
}
|
||||
|
||||
const int fd = ::open(path.c_str(), flags, perm);
|
||||
const int fd = ::open(path.c_str(), flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
|
||||
if (fd == -1)
|
||||
{
|
||||
@@ -1076,19 +902,6 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode & fs::write && mode & (fs::lock + fs::unread) && ::flock(fd, LOCK_EX | LOCK_NB) != 0)
|
||||
{
|
||||
g_tls_error = errno == EWOULDBLOCK ? fs::error::acces : to_error(errno);
|
||||
::close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode & fs::trunc && mode & (fs::lock + fs::unread))
|
||||
{
|
||||
// Postpone truncation in order to avoid using O_TRUNC on a locked file
|
||||
::ftruncate(fd, 0);
|
||||
}
|
||||
|
||||
class unix_file final : public file_base
|
||||
{
|
||||
const int m_fd;
|
||||
@@ -1115,10 +928,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
info.size = file_info.st_size;
|
||||
info.atime = file_info.st_atime;
|
||||
info.mtime = file_info.st_mtime;
|
||||
info.ctime = info.mtime;
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
info.ctime = file_info.st_ctime;
|
||||
|
||||
return info;
|
||||
}
|
||||
@@ -1164,7 +974,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
(fmt::throw_exception("Invalid whence (0x%x)" HERE, whence), 0);
|
||||
|
||||
const auto result = ::lseek(m_fd, offset, mode);
|
||||
|
||||
|
||||
if (result == -1)
|
||||
{
|
||||
g_tls_error = to_error(errno);
|
||||
@@ -1181,22 +991,6 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
|
||||
return file_info.st_size;
|
||||
}
|
||||
|
||||
native_handle get_handle() override
|
||||
{
|
||||
return m_fd;
|
||||
}
|
||||
|
||||
u64 write_gather(const iovec_clone* buffers, u64 buf_count) override
|
||||
{
|
||||
static_assert(sizeof(iovec) == sizeof(iovec_clone), "Weird iovec size");
|
||||
static_assert(offsetof(iovec, iov_len) == offsetof(iovec_clone, iov_len), "Weird iovec::iov_len offset");
|
||||
|
||||
const auto result = ::writev(m_fd, (const iovec*)buffers, buf_count);
|
||||
verify("file::write_gather" HERE), result != -1;
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
m_file = std::make_unique<unix_file>(fd);
|
||||
@@ -1272,29 +1066,6 @@ fs::file::file(const void* ptr, std::size_t size)
|
||||
m_file = std::make_unique<memory_stream>(ptr, size);
|
||||
}
|
||||
|
||||
fs::native_handle fs::file::get_handle() const
|
||||
{
|
||||
if (m_file)
|
||||
{
|
||||
return m_file->get_handle();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
return INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
bool fs::file::set_delete(bool autodelete) const
|
||||
{
|
||||
FILE_DISPOSITION_INFO disp;
|
||||
disp.DeleteFileW = autodelete;
|
||||
return SetFileInformationByHandle(get_handle(), FileDispositionInfo, &disp, sizeof(disp)) != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void fs::dir::xnull() const
|
||||
{
|
||||
fmt::throw_exception<std::logic_error>("fs::dir is null");
|
||||
@@ -1302,13 +1073,6 @@ void fs::dir::xnull() const
|
||||
|
||||
bool fs::dir::open(const std::string& path)
|
||||
{
|
||||
if (path.empty())
|
||||
{
|
||||
// Don't allow opening empty path (TODO)
|
||||
g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto device = get_virtual_device(path))
|
||||
{
|
||||
if (auto&& _dir = device->open_dir(path))
|
||||
@@ -1345,10 +1109,7 @@ bool fs::dir::open(const std::string& path)
|
||||
info.size = ((u64)found.nFileSizeHigh << 32) | (u64)found.nFileSizeLow;
|
||||
info.atime = to_time(found.ftLastAccessTime);
|
||||
info.mtime = to_time(found.ftLastWriteTime);
|
||||
info.ctime = info.mtime;
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
info.ctime = to_time(found.ftCreationTime);
|
||||
|
||||
m_entries.emplace_back(std::move(info));
|
||||
}
|
||||
@@ -1418,12 +1179,7 @@ bool fs::dir::open(const std::string& path)
|
||||
}
|
||||
|
||||
struct ::stat file_info;
|
||||
|
||||
if (::fstatat(::dirfd(m_dd), found->d_name, &file_info, 0) != 0)
|
||||
{
|
||||
//failed metadata (broken symlink?), ignore and skip to next file
|
||||
return read(info);
|
||||
}
|
||||
verify("dir::read" HERE), ::fstatat(::dirfd(m_dd), found->d_name, &file_info, 0) == 0;
|
||||
|
||||
info.name = found->d_name;
|
||||
info.is_directory = S_ISDIR(file_info.st_mode);
|
||||
@@ -1431,10 +1187,7 @@ bool fs::dir::open(const std::string& path)
|
||||
info.size = file_info.st_size;
|
||||
info.atime = file_info.st_atime;
|
||||
info.mtime = file_info.st_mtime;
|
||||
info.ctime = info.mtime;
|
||||
|
||||
if (info.atime < info.mtime)
|
||||
info.atime = info.mtime;
|
||||
info.ctime = file_info.st_ctime;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1459,10 +1212,8 @@ const std::string& fs::get_config_dir()
|
||||
std::string dir;
|
||||
|
||||
#ifdef _WIN32
|
||||
wchar_t buf[32768];
|
||||
constexpr DWORD size = static_cast<DWORD>(std::size(buf));
|
||||
if (GetEnvironmentVariable(L"RPCS3_CONFIG_DIR", buf, size) - 1 >= size - 1 &&
|
||||
GetModuleFileName(NULL, buf, size) - 1 >= size - 1)
|
||||
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
|
||||
@@ -1474,22 +1225,16 @@ const std::string& fs::get_config_dir()
|
||||
|
||||
dir.resize(dir.rfind('/') + 1);
|
||||
#else
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (const char* home = ::getenv("HOME"))
|
||||
dir = home + "/Library/Application Support"s;
|
||||
#else
|
||||
if (const char* conf = ::getenv("XDG_CONFIG_HOME"))
|
||||
dir = conf;
|
||||
if (const char* home = ::getenv("XDG_CONFIG_HOME"))
|
||||
dir = home;
|
||||
else if (const char* home = ::getenv("HOME"))
|
||||
dir = home + "/.config"s;
|
||||
#endif
|
||||
else // Just in case
|
||||
dir = "./config";
|
||||
|
||||
dir += "/rpcs3/";
|
||||
|
||||
if (!create_path(dir))
|
||||
if (!is_dir(dir) && !create_path(dir))
|
||||
{
|
||||
std::printf("Failed to create configuration directory '%s' (%d).\n", dir.c_str(), errno);
|
||||
}
|
||||
@@ -1501,83 +1246,102 @@ const std::string& fs::get_config_dir()
|
||||
return s_dir;
|
||||
}
|
||||
|
||||
const std::string& fs::get_cache_dir()
|
||||
std::string fs::get_data_dir(const std::string& prefix, const std::string& location, const std::string& suffix)
|
||||
{
|
||||
static const std::string s_dir = []
|
||||
{
|
||||
std::string dir;
|
||||
const std::string& dir = get_config_dir() + "data/";
|
||||
|
||||
#ifdef _WIN32
|
||||
dir = get_config_dir();
|
||||
#else
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (const char* home = ::getenv("HOME"))
|
||||
dir = home + "/Library/Caches"s;
|
||||
#else
|
||||
if (const char* cache = ::getenv("XDG_CACHE_HOME"))
|
||||
dir = cache;
|
||||
else if (const char* conf = ::getenv("XDG_CONFIG_HOME"))
|
||||
dir = conf;
|
||||
else if (const char* home = ::getenv("HOME"))
|
||||
dir = home + "/.cache"s;
|
||||
#endif
|
||||
else // Just in case
|
||||
dir = "./cache";
|
||||
|
||||
dir += "/rpcs3/";
|
||||
|
||||
if (!create_path(dir))
|
||||
if (!is_dir(dir) && !create_path(dir))
|
||||
{
|
||||
std::printf("Failed to create configuration directory '%s' (%d).\n", dir.c_str(), errno);
|
||||
return get_config_dir();
|
||||
}
|
||||
#endif
|
||||
|
||||
return dir;
|
||||
}();
|
||||
|
||||
return s_dir;
|
||||
}
|
||||
std::vector<u8> buf;
|
||||
buf.reserve(location.size() + 1);
|
||||
|
||||
bool fs::remove_all(const std::string& path, bool remove_root)
|
||||
{
|
||||
if (const auto root_dir = dir(path))
|
||||
// Normalize location
|
||||
for (char c : location)
|
||||
{
|
||||
for (const auto& entry : root_dir)
|
||||
#ifdef _WIN32
|
||||
if (c == '/' || c == '\\')
|
||||
#else
|
||||
if (c == '/')
|
||||
#endif
|
||||
{
|
||||
if (entry.name == "." || entry.name == "..")
|
||||
if (buf.empty() || buf.back() != '/')
|
||||
{
|
||||
continue;
|
||||
buf.push_back('/');
|
||||
}
|
||||
|
||||
if (entry.is_directory == false)
|
||||
{
|
||||
if (!remove_file(path + '/' + entry.name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
buf.push_back(c);
|
||||
}
|
||||
|
||||
if (entry.is_directory == true)
|
||||
{
|
||||
if (!remove_all(path + '/' + entry.name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Calculate hash
|
||||
u8 hash[20];
|
||||
sha1(buf.data(), buf.size(), hash);
|
||||
|
||||
// Concatenate
|
||||
std::string&& result = fmt::format("%s%s/%016llx%08x-%s/", s_dir, prefix, reinterpret_cast<be_t<u64>&>(hash[0]), reinterpret_cast<be_t<u32>&>(hash[8]), suffix);
|
||||
|
||||
if (!is_dir(result))
|
||||
{
|
||||
// Create dir if necessary
|
||||
if (create_path(result))
|
||||
{
|
||||
// Acknowledge original location
|
||||
file(result + ".location", rewrite).write(buf);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string fs::get_data_dir(const std::string& prefix, const std::string& path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const auto& delim = "/\\";
|
||||
#else
|
||||
const auto& delim = "/";
|
||||
#endif
|
||||
|
||||
// Extract file name and location
|
||||
const std::string& location = fs::get_parent_dir(path);
|
||||
const std::size_t name_pos = path.find_first_not_of(delim, location.size());
|
||||
|
||||
return fs::get_data_dir(prefix, location, name_pos == -1 ? std::string{} : path.substr(name_pos));
|
||||
}
|
||||
|
||||
void fs::remove_all(const std::string& path, bool remove_root)
|
||||
{
|
||||
for (const auto& entry : dir(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (remove_root)
|
||||
{
|
||||
return remove_dir(path);
|
||||
if (entry.name == "." || entry.name == "..")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.is_directory == false)
|
||||
{
|
||||
remove_file(path + '/' + entry.name);
|
||||
}
|
||||
|
||||
if (entry.is_directory == true)
|
||||
{
|
||||
remove_all(path + '/' + entry.name);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
if (remove_root)
|
||||
{
|
||||
remove_dir(path);
|
||||
}
|
||||
}
|
||||
|
||||
u64 fs::get_dir_size(const std::string& path)
|
||||
@@ -1605,122 +1369,6 @@ u64 fs::get_dir_size(const std::string& path)
|
||||
return result;
|
||||
}
|
||||
|
||||
fs::file fs::make_gather(std::vector<fs::file> files)
|
||||
{
|
||||
struct gather_stream : file_base
|
||||
{
|
||||
u64 pos = 0;
|
||||
u64 end = 0;
|
||||
std::vector<file> files;
|
||||
std::map<u64, u64> ends; // Fragment End Offset -> Index
|
||||
|
||||
gather_stream(std::vector<fs::file> arg)
|
||||
: files(std::move(arg))
|
||||
{
|
||||
// Preprocess files
|
||||
for (auto&& f : files)
|
||||
{
|
||||
end += f.size();
|
||||
ends.emplace(end, ends.size());
|
||||
}
|
||||
}
|
||||
|
||||
~gather_stream() override
|
||||
{
|
||||
}
|
||||
|
||||
fs::stat_t stat() override
|
||||
{
|
||||
fs::stat_t result{};
|
||||
|
||||
if (!files.empty())
|
||||
{
|
||||
result = files[0].stat();
|
||||
}
|
||||
|
||||
result.is_directory = false;
|
||||
result.is_writable = false;
|
||||
result.size = end;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool trunc(u64 length) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 read(void* buffer, u64 size) override
|
||||
{
|
||||
if (pos < end)
|
||||
{
|
||||
// Current pos
|
||||
const u64 start = pos;
|
||||
|
||||
// Get readable size
|
||||
if (const u64 max = std::min<u64>(size, end - pos))
|
||||
{
|
||||
u8* buf_out = static_cast<u8*>(buffer);
|
||||
u64 buf_max = max;
|
||||
|
||||
for (auto it = ends.upper_bound(pos); it != ends.end(); ++it)
|
||||
{
|
||||
// Set position for the fragment
|
||||
files[it->second].seek(pos - it->first, fs::seek_end);
|
||||
|
||||
const u64 count = std::min<u64>(it->first - pos, buf_max);
|
||||
const u64 read = files[it->second].read(buf_out, count);
|
||||
|
||||
buf_out += count;
|
||||
buf_max -= count;
|
||||
pos += read;
|
||||
|
||||
if (read < count || buf_max == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pos - start;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 write(const void* buffer, u64 size) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
u64 seek(s64 offset, seek_mode whence) override
|
||||
{
|
||||
const s64 new_pos =
|
||||
whence == fs::seek_set ? offset :
|
||||
whence == fs::seek_cur ? offset + pos :
|
||||
whence == fs::seek_end ? offset + end :
|
||||
(fmt::raw_error("fs::gather_stream::seek(): invalid whence"), 0);
|
||||
|
||||
if (new_pos < 0)
|
||||
{
|
||||
fs::g_tls_error = fs::error::inval;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos = new_pos;
|
||||
return pos;
|
||||
}
|
||||
|
||||
u64 size() override
|
||||
{
|
||||
return end;
|
||||
}
|
||||
};
|
||||
|
||||
fs::file result;
|
||||
result.reset(std::make_unique<gather_stream>(std::move(files)));
|
||||
return result;
|
||||
}
|
||||
|
||||
template<>
|
||||
void fmt_class_string<fs::seek_mode>::format(std::string& out, u64 arg)
|
||||
{
|
||||
@@ -1750,7 +1398,6 @@ void fmt_class_string<fs::error>::format(std::string& out, u64 arg)
|
||||
case fs::error::noent: return "Not found";
|
||||
case fs::error::exist: return "Already exists";
|
||||
case fs::error::acces: return "Access violation";
|
||||
case fs::error::notempty: return "Not empty";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
|
||||
+17
-51
@@ -10,12 +10,6 @@
|
||||
|
||||
namespace fs
|
||||
{
|
||||
#ifdef _WIN32
|
||||
using native_handle = void*;
|
||||
#else
|
||||
using native_handle = int;
|
||||
#endif
|
||||
|
||||
// File open mode flags
|
||||
enum class open_mode : u32
|
||||
{
|
||||
@@ -25,8 +19,6 @@ namespace fs
|
||||
create,
|
||||
trunc,
|
||||
excl,
|
||||
lock,
|
||||
unread,
|
||||
|
||||
__bitset_enum_max
|
||||
};
|
||||
@@ -37,8 +29,6 @@ namespace fs
|
||||
constexpr auto create = +open_mode::create; // Create file if it doesn't exist
|
||||
constexpr auto trunc = +open_mode::trunc; // Clear opened file if it's not empty
|
||||
constexpr auto excl = +open_mode::excl; // Failure if the file already exists (used with `create`)
|
||||
constexpr auto lock = +open_mode::lock; // Prevent opening the file more than once
|
||||
constexpr auto unread = +open_mode::unread; // Aggressively prevent reading the opened file (do not use)
|
||||
|
||||
constexpr auto rewrite = open_mode::write + open_mode::create + open_mode::trunc;
|
||||
|
||||
@@ -65,13 +55,6 @@ namespace fs
|
||||
s64 ctime;
|
||||
};
|
||||
|
||||
// Helper, layout is equal to iovec struct
|
||||
struct iovec_clone
|
||||
{
|
||||
const void* iov_base;
|
||||
std::size_t iov_len;
|
||||
};
|
||||
|
||||
// File handle base
|
||||
struct file_base
|
||||
{
|
||||
@@ -84,8 +67,6 @@ namespace fs
|
||||
virtual u64 write(const void* buffer, u64 size) = 0;
|
||||
virtual u64 seek(s64 offset, seek_mode whence) = 0;
|
||||
virtual u64 size() = 0;
|
||||
virtual native_handle get_handle();
|
||||
virtual u64 write_gather(const iovec_clone* buffers, u64 buf_count);
|
||||
};
|
||||
|
||||
// Directory entry (TODO)
|
||||
@@ -164,7 +145,7 @@ namespace fs
|
||||
bool create_path(const std::string& path);
|
||||
|
||||
// Rename (move) file or directory
|
||||
bool rename(const std::string& from, const std::string& to, bool overwrite);
|
||||
bool rename(const std::string& from, const std::string& to);
|
||||
|
||||
// Copy file contents
|
||||
bool copy_file(const std::string& from, const std::string& to, bool overwrite);
|
||||
@@ -190,7 +171,7 @@ namespace fs
|
||||
file() = default;
|
||||
|
||||
// Open file with specified mode
|
||||
explicit file(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);
|
||||
@@ -366,21 +347,6 @@ namespace fs
|
||||
if (seek(0), !read(result)) xfail();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get native handle if available
|
||||
native_handle get_handle() const;
|
||||
|
||||
// Gathered write
|
||||
u64 write_gather(const iovec_clone* buffers, u64 buf_count) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
return m_file->write_gather(buffers, buf_count);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows-specific function
|
||||
bool set_delete(bool autodelete = true) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
class dir final
|
||||
@@ -417,7 +383,7 @@ namespace fs
|
||||
{
|
||||
m_dir = std::move(ptr);
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<dir_base> release()
|
||||
{
|
||||
return std::move(m_dir);
|
||||
@@ -439,7 +405,7 @@ namespace fs
|
||||
|
||||
class iterator
|
||||
{
|
||||
const dir* m_parent;
|
||||
dir* m_parent;
|
||||
dir_entry m_entry;
|
||||
|
||||
public:
|
||||
@@ -449,7 +415,7 @@ namespace fs
|
||||
from_current
|
||||
};
|
||||
|
||||
iterator(const dir* parent, mode mode_ = mode::from_first)
|
||||
iterator(dir* parent, mode mode_ = mode::from_first)
|
||||
: m_parent(parent)
|
||||
{
|
||||
if (!m_parent)
|
||||
@@ -475,7 +441,7 @@ namespace fs
|
||||
|
||||
iterator& operator++()
|
||||
{
|
||||
*this = {m_parent, mode::from_current};
|
||||
*this = { m_parent, mode::from_current };
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -485,25 +451,28 @@ namespace fs
|
||||
}
|
||||
};
|
||||
|
||||
iterator begin() const
|
||||
iterator begin()
|
||||
{
|
||||
return {m_dir ? this : nullptr};
|
||||
return{ m_dir ? this : nullptr };
|
||||
}
|
||||
|
||||
iterator end() const
|
||||
iterator end()
|
||||
{
|
||||
return {nullptr};
|
||||
return{ nullptr };
|
||||
}
|
||||
};
|
||||
|
||||
// Get configuration directory
|
||||
const std::string& get_config_dir();
|
||||
|
||||
// Get common cache directory
|
||||
const std::string& get_cache_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);
|
||||
|
||||
// Get data/cache directory for specified prefix and path (suffix will be filename)
|
||||
std::string get_data_dir(const std::string& prefix, const std::string& path);
|
||||
|
||||
// Delete directory and all its contents recursively
|
||||
bool remove_all(const std::string& path, bool remove_root = true);
|
||||
void remove_all(const std::string& path, bool remove_root = true);
|
||||
|
||||
// Get size of all files recursively
|
||||
u64 get_dir_size(const std::string& path);
|
||||
@@ -516,7 +485,6 @@ namespace fs
|
||||
noent,
|
||||
exist,
|
||||
acces,
|
||||
notempty,
|
||||
};
|
||||
|
||||
// Error code returned
|
||||
@@ -631,12 +599,10 @@ namespace fs
|
||||
if (fs::file f{path, mode})
|
||||
{
|
||||
// Write args sequentially
|
||||
(f.write(args), ...);
|
||||
int seq[]{ (f.write(args), 0)... };
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
file make_gather(std::vector<file>);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "GDBDebugServer.h"
|
||||
#include "Log.h"
|
||||
#include <algorithm>
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/CPU/CPUThread.h"
|
||||
@@ -13,13 +13,13 @@
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include "fcntl.h"
|
||||
#include"fcntl.h"
|
||||
#endif
|
||||
|
||||
extern void ppu_set_breakpoint(u32 addr);
|
||||
extern void ppu_remove_breakpoint(u32 addr);
|
||||
|
||||
LOG_CHANNEL(gdbDebugServer);
|
||||
logs::channel gdbDebugServer("gdbDebugServer");
|
||||
|
||||
int sock_init(void)
|
||||
{
|
||||
@@ -166,10 +166,9 @@ char GDBDebugServer::read_char()
|
||||
|
||||
u8 GDBDebugServer::read_hexbyte()
|
||||
{
|
||||
std::string s = "";
|
||||
s += read_char();
|
||||
s += read_char();
|
||||
return hex_to_u8(s);
|
||||
char buf[2];
|
||||
read(buf, 2);
|
||||
return static_cast<u8>(strtol(buf, nullptr, 16));
|
||||
}
|
||||
|
||||
void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
|
||||
@@ -177,7 +176,7 @@ void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
|
||||
char c = read_char();
|
||||
//interrupt
|
||||
if (UNLIKELY(c == 0x03)) {
|
||||
out_cmd.cmd = '\x03';
|
||||
out_cmd.cmd = "\0x03";
|
||||
out_cmd.data = "";
|
||||
out_cmd.checksum = 0;
|
||||
return;
|
||||
@@ -224,7 +223,7 @@ void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
|
||||
}
|
||||
out_cmd.checksum = read_hexbyte();
|
||||
if (out_cmd.checksum != checksum) {
|
||||
throw wrong_checksum_exception("Wrong checksum for packet" HERE);
|
||||
throw new wrong_checksum_exception("Wrong checksum for packet" HERE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,7 +337,6 @@ bool GDBDebugServer::select_thread(u64 id)
|
||||
selected_thread = ppu.ptr;
|
||||
return true;
|
||||
}
|
||||
gdbDebugServer.warning("Unable to select thread! Is the emulator running?");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -354,7 +352,7 @@ std::string GDBDebugServer::get_reg(std::shared_ptr<ppu_thread> thread, u32 rid)
|
||||
case 65:
|
||||
return std::string(16, 'x');
|
||||
case 66:
|
||||
return u32_to_padded_hex(thread->cr.pack());
|
||||
return u32_to_padded_hex(thread->cr_pack());
|
||||
case 67:
|
||||
return u64_to_padded_hex(thread->lr);
|
||||
case 68:
|
||||
@@ -367,8 +365,8 @@ std::string GDBDebugServer::get_reg(std::shared_ptr<ppu_thread> thread, u32 rid)
|
||||
return std::string(8, 'x');
|
||||
default:
|
||||
if (rid > 70) return "";
|
||||
return (rid > 31)
|
||||
? u64_to_padded_hex(std::bit_cast<u64>(thread->fpr[rid - 32])) //fpr
|
||||
return (rid > 31)
|
||||
? u64_to_padded_hex(reinterpret_cast<u64&>(thread->fpr[rid - 32])) //fpr
|
||||
: u64_to_padded_hex(thread->gpr[rid]); //gpr
|
||||
}
|
||||
}
|
||||
@@ -383,7 +381,7 @@ bool GDBDebugServer::set_reg(std::shared_ptr<ppu_thread> thread, u32 rid, std::s
|
||||
case 65:
|
||||
return true;
|
||||
case 66:
|
||||
thread->cr.unpack(hex_to_u32(value));
|
||||
thread->cr_unpack(hex_to_u32(value));
|
||||
return true;
|
||||
case 67:
|
||||
thread->lr = hex_to_u64(value);
|
||||
@@ -401,7 +399,7 @@ bool GDBDebugServer::set_reg(std::shared_ptr<ppu_thread> thread, u32 rid, std::s
|
||||
if (rid > 70) return false;
|
||||
if (rid > 31) {
|
||||
u64 val = hex_to_u64(value);
|
||||
thread->fpr[rid - 32] = std::bit_cast<f64>(val);
|
||||
thread->fpr[rid - 32] = reinterpret_cast<f64&>(val);
|
||||
} else {
|
||||
thread->gpr[rid] = hex_to_u64(value);
|
||||
}
|
||||
@@ -429,25 +427,6 @@ bool GDBDebugServer::send_reason()
|
||||
return send_cmd_ack("S05");
|
||||
}
|
||||
|
||||
void GDBDebugServer::wait_with_interrupts() {
|
||||
char c;
|
||||
while (!paused) {
|
||||
int result = recv(client_socket, &c, 1, 0);
|
||||
|
||||
if (result == SOCKET_ERROR) {
|
||||
if (check_errno_again()) {
|
||||
thread_ctrl::wait_for(50);
|
||||
continue;
|
||||
}
|
||||
|
||||
gdbDebugServer.error("Error during socket read");
|
||||
fmt::throw_exception("Error during socket read" HERE);
|
||||
} else if (c == 0x03) {
|
||||
paused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_extended_mode(gdb_cmd & cmd)
|
||||
{
|
||||
return send_cmd_ack("OK");
|
||||
@@ -474,25 +453,24 @@ bool GDBDebugServer::cmd_thread_info(gdb_cmd & cmd)
|
||||
result += u64_to_padded_hex(static_cast<u64>(cpu.id));
|
||||
};
|
||||
idm::select<ppu_thread>(on_select);
|
||||
//idm::select<RawSPUThread>(on_select);
|
||||
//idm::select<SPUThread>(on_select);
|
||||
idm::select<ARMv7Thread>(on_select);
|
||||
idm::select<RawSPUThread>(on_select);
|
||||
idm::select<SPUThread>(on_select);
|
||||
|
||||
//todo: this may exceed max command length
|
||||
result = "m" + result + "l";
|
||||
|
||||
|
||||
return send_cmd_ack(result);;
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_current_thread(gdb_cmd & cmd)
|
||||
{
|
||||
return send_cmd_ack(selected_thread.expired() ? "" : ("QC" + u64_to_padded_hex(selected_thread.lock()->id)));
|
||||
return send_cmd_ack(selected_thread.expired() ? "" : u64_to_padded_hex(selected_thread.lock()->id));
|
||||
}
|
||||
|
||||
bool GDBDebugServer::cmd_read_register(gdb_cmd & cmd)
|
||||
{
|
||||
if (!select_thread(general_ops_thread_id)) {
|
||||
return send_cmd_ack("E02");
|
||||
}
|
||||
select_thread(general_ops_thread_id);
|
||||
auto th = selected_thread.lock();
|
||||
if (th->id_type() == 1) {
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||
@@ -510,9 +488,7 @@ bool GDBDebugServer::cmd_read_register(gdb_cmd & cmd)
|
||||
|
||||
bool GDBDebugServer::cmd_write_register(gdb_cmd & cmd)
|
||||
{
|
||||
if (!select_thread(general_ops_thread_id)) {
|
||||
return send_cmd_ack("E02");
|
||||
}
|
||||
select_thread(general_ops_thread_id);
|
||||
auto th = selected_thread.lock();
|
||||
if (th->id_type() == 1) {
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||
@@ -541,7 +517,7 @@ bool GDBDebugServer::cmd_read_memory(gdb_cmd & cmd)
|
||||
std::string result;
|
||||
result.reserve(len * 2);
|
||||
for (u32 i = 0; i < len; ++i) {
|
||||
if (vm::check_addr(addr)) {
|
||||
if (vm::check_addr(addr + i)) {
|
||||
result += to_hexbyte(vm::read8(addr + i));
|
||||
} else {
|
||||
break;
|
||||
@@ -567,7 +543,7 @@ bool GDBDebugServer::cmd_write_memory(gdb_cmd & cmd)
|
||||
u32 len = hex_to_u32(cmd.data.substr(s + 1, s2 - s - 1));
|
||||
const char* data_ptr = (cmd.data.c_str()) + s2 + 1;
|
||||
for (u32 i = 0; i < len; ++i) {
|
||||
if (vm::check_addr(addr + i, 1, vm::page_writable)) {
|
||||
if (vm::check_addr(addr + i)) {
|
||||
u8 val;
|
||||
int res = sscanf_s(data_ptr, "%02hhX", &val);
|
||||
if (!res) {
|
||||
@@ -658,31 +634,30 @@ bool GDBDebugServer::cmd_vcont(gdb_cmd & cmd)
|
||||
{
|
||||
//todo: handle multiple actions and thread ids
|
||||
this->from_breakpoint = false;
|
||||
if (cmd.data[1] == 'c' || cmd.data[1] == 's') {
|
||||
if (cmd.data[1] == 'c') {
|
||||
select_thread(continue_ops_thread_id);
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||
paused = false;
|
||||
if (cmd.data[1] == 's') {
|
||||
ppu->state += cpu_flag::dbg_step;
|
||||
}
|
||||
ppu->state -= cpu_flag::dbg_pause;
|
||||
//special case if app didn't start yet (only loaded)
|
||||
if (!Emu.IsPaused() && !Emu.IsRunning()) {
|
||||
Emu.Run();
|
||||
if (Emu.IsPaused()) {
|
||||
Emu.Resume();
|
||||
}
|
||||
thread_ctrl::wait();
|
||||
//we are in all-stop mode
|
||||
Emu.Pause();
|
||||
return send_reason();
|
||||
} else if (cmd.data[1] == 's') {
|
||||
select_thread(continue_ops_thread_id);
|
||||
auto ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||
ppu->state += cpu_flag::dbg_step;
|
||||
ppu->state -= cpu_flag::dbg_pause;
|
||||
if (Emu.IsPaused()) {
|
||||
Emu.Resume();
|
||||
} else {
|
||||
ppu->notify();
|
||||
}
|
||||
wait_with_interrupts();
|
||||
thread_ctrl::wait();
|
||||
//we are in all-stop mode
|
||||
Emu.Pause();
|
||||
select_thread(pausedBy);
|
||||
// we have to remove dbg_pause from thread that paused execution, otherwise
|
||||
// it will be paused forever (Emu.Resume only removes dbg_global_pause)
|
||||
ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||
ppu->state -= cpu_flag::dbg_pause;
|
||||
return send_reason();
|
||||
}
|
||||
return send_cmd_ack("");
|
||||
@@ -754,9 +729,7 @@ void GDBDebugServer::on_task()
|
||||
return;
|
||||
}
|
||||
//stop immediately
|
||||
if (Emu.IsRunning()) {
|
||||
Emu.Pause();
|
||||
}
|
||||
Emu.Pause();
|
||||
|
||||
try {
|
||||
char hostbuf[32];
|
||||
@@ -795,7 +768,7 @@ void GDBDebugServer::on_task()
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::runtime_error& e)
|
||||
catch (std::runtime_error& e)
|
||||
{
|
||||
if (client_socket) {
|
||||
closesocket(client_socket);
|
||||
@@ -829,16 +802,7 @@ void GDBDebugServer::on_stop()
|
||||
this->stop = true;
|
||||
//just in case we are waiting for breakpoint
|
||||
this->notify();
|
||||
old_thread::on_stop();
|
||||
}
|
||||
|
||||
void GDBDebugServer::pause_from(cpu_thread* t) {
|
||||
if (paused) {
|
||||
return;
|
||||
}
|
||||
paused = true;
|
||||
pausedBy = t->id;
|
||||
notify();
|
||||
named_thread::on_stop();
|
||||
}
|
||||
|
||||
u32 g_gdb_debugger_id = 0;
|
||||
|
||||
+15
-11
@@ -40,8 +40,8 @@ public:
|
||||
const u64 ALL_THREADS = 0xffffffffffffffff;
|
||||
const u64 ANY_THREAD = 0;
|
||||
|
||||
class GDBDebugServer
|
||||
{
|
||||
class GDBDebugServer : public named_thread {
|
||||
|
||||
socket_t server_socket;
|
||||
socket_t client_socket;
|
||||
std::weak_ptr<cpu_thread> selected_thread;
|
||||
@@ -50,7 +50,7 @@ class GDBDebugServer
|
||||
|
||||
//initialize server socket and start listening
|
||||
void start_server();
|
||||
//read at most cnt bytes to buf, returns number of bytes actually read
|
||||
//read at most cnt bytes to buf, returns nubmer of bytes actually read
|
||||
int read(void* buf, int cnt);
|
||||
//reads one character
|
||||
char read_char();
|
||||
@@ -60,8 +60,8 @@ class GDBDebugServer
|
||||
void try_read_cmd(gdb_cmd& out_cmd);
|
||||
//reads commands until receiveing one with valid checksum
|
||||
//in case of other exception (i.e. wrong first char of command)
|
||||
//it will log exception text and return false
|
||||
//in that case best for caller would be to stop reading, because
|
||||
//it will log exception text and return false
|
||||
//in that case best for caller would be to stop reading, because
|
||||
//chance of getting correct command is low
|
||||
bool read_cmd(gdb_cmd& out_cmd);
|
||||
//send cnt bytes from buf to client
|
||||
@@ -90,8 +90,6 @@ class GDBDebugServer
|
||||
//send reason of stop, returns false if sending response failed
|
||||
bool send_reason();
|
||||
|
||||
void wait_with_interrupts();
|
||||
|
||||
//commands
|
||||
bool cmd_extended_mode(gdb_cmd& cmd);
|
||||
bool cmd_reason(gdb_cmd& cmd);
|
||||
@@ -112,14 +110,20 @@ class GDBDebugServer
|
||||
bool cmd_set_breakpoint(gdb_cmd& cmd);
|
||||
bool cmd_remove_breakpoint(gdb_cmd& cmd);
|
||||
|
||||
protected:
|
||||
void on_task() override final;
|
||||
void on_exit() override final;
|
||||
|
||||
public:
|
||||
static const u32 id_base = 1;
|
||||
static const u32 id_step = 1;
|
||||
static const u32 id_count = 0x100000;
|
||||
|
||||
bool from_breakpoint = true;
|
||||
bool stop = false;
|
||||
bool paused = false;
|
||||
u64 pausedBy;
|
||||
|
||||
void operator()();
|
||||
void pause_from(cpu_thread* t);
|
||||
virtual std::string get_name() const;
|
||||
virtual void on_stop() override final;
|
||||
};
|
||||
|
||||
extern u32 g_gdb_debugger_id;
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma push_macro("new")
|
||||
#undef new
|
||||
#include <gsl/gsl>
|
||||
#include <gsl.h>
|
||||
#pragma pop_macro("new")
|
||||
#undef Expects
|
||||
#undef Ensures
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
template<typename T1, typename T2> struct range_t
|
||||
{
|
||||
T1 _min; // first value
|
||||
|
||||
+73
-440
@@ -1,232 +1,3 @@
|
||||
#include "types.h"
|
||||
#include "JIT.h"
|
||||
#include "StrFmt.h"
|
||||
#include "File.h"
|
||||
#include "Log.h"
|
||||
#include "mutex.h"
|
||||
#include "sysinfo.h"
|
||||
#include "VirtualMemory.h"
|
||||
#include <immintrin.h>
|
||||
|
||||
// Memory manager mutex
|
||||
shared_mutex s_mutex2;
|
||||
|
||||
#ifdef __linux__
|
||||
#define CAN_OVERCOMMIT
|
||||
#endif
|
||||
|
||||
static u8* get_jit_memory()
|
||||
{
|
||||
// Reserve 2G memory (magic static)
|
||||
static void* const s_memory2 = []() -> void*
|
||||
{
|
||||
void* ptr = utils::memory_reserve(0x80000000);
|
||||
|
||||
#ifdef CAN_OVERCOMMIT
|
||||
utils::memory_commit(ptr, 0x80000000);
|
||||
utils::memory_protect(ptr, 0x40000000, utils::protection::wx);
|
||||
#endif
|
||||
return ptr;
|
||||
}();
|
||||
|
||||
return static_cast<u8*>(s_memory2);
|
||||
}
|
||||
|
||||
// Allocation counters (1G code, 1G data subranges)
|
||||
static atomic_t<u64> s_code_pos{0}, s_data_pos{0};
|
||||
|
||||
// Snapshot of code generated before main()
|
||||
static std::vector<u8> s_code_init, s_data_init;
|
||||
|
||||
template <atomic_t<u64>& Ctr, uint Off, utils::protection Prot>
|
||||
static u8* add_jit_memory(std::size_t size, uint align)
|
||||
{
|
||||
// Select subrange
|
||||
u8* pointer = get_jit_memory() + Off;
|
||||
|
||||
if (UNLIKELY(!size && !align))
|
||||
{
|
||||
// Return subrange info
|
||||
return pointer;
|
||||
}
|
||||
|
||||
#ifndef CAN_OVERCOMMIT
|
||||
std::lock_guard lock(s_mutex2);
|
||||
#endif
|
||||
|
||||
u64 olda, newa;
|
||||
|
||||
// Simple allocation by incrementing pointer to the next free data
|
||||
const u64 pos = Ctr.atomic_op([&](u64& ctr) -> u64
|
||||
{
|
||||
const u64 _pos = ::align(ctr, align);
|
||||
const u64 _new = ::align(_pos + size, align);
|
||||
|
||||
if (UNLIKELY(_new > 0x40000000))
|
||||
{
|
||||
// Sorry, we failed, and further attempts should fail too.
|
||||
ctr = 0x40000000;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check the necessity to commit more memory
|
||||
olda = ::align(ctr, 0x10000);
|
||||
newa = ::align(_new, 0x10000);
|
||||
|
||||
ctr = _new;
|
||||
return _pos;
|
||||
});
|
||||
|
||||
if (UNLIKELY(pos == -1))
|
||||
{
|
||||
LOG_WARNING(GENERAL, "JIT: Out of memory (size=0x%x, align=0x%x, off=0x%x)", size, align, Off);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (UNLIKELY(olda != newa))
|
||||
{
|
||||
#ifdef CAN_OVERCOMMIT
|
||||
// TODO: possibly madvise
|
||||
#else
|
||||
// Commit more memory
|
||||
utils::memory_commit(pointer + olda, newa - olda, Prot);
|
||||
#endif
|
||||
}
|
||||
|
||||
return pointer + pos;
|
||||
}
|
||||
|
||||
jit_runtime::jit_runtime()
|
||||
: HostRuntime()
|
||||
{
|
||||
}
|
||||
|
||||
jit_runtime::~jit_runtime()
|
||||
{
|
||||
}
|
||||
|
||||
asmjit::Error jit_runtime::_add(void** dst, asmjit::CodeHolder* code) noexcept
|
||||
{
|
||||
std::size_t codeSize = code->getCodeSize();
|
||||
if (UNLIKELY(!codeSize))
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorNoCodeGenerated;
|
||||
}
|
||||
|
||||
void* p = jit_runtime::alloc(codeSize, 16);
|
||||
if (UNLIKELY(!p))
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorNoVirtualMemory;
|
||||
}
|
||||
|
||||
std::size_t relocSize = code->relocate(p);
|
||||
if (UNLIKELY(!relocSize))
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorInvalidState;
|
||||
}
|
||||
|
||||
flush(p, relocSize);
|
||||
*dst = p;
|
||||
|
||||
return asmjit::kErrorOk;
|
||||
}
|
||||
|
||||
asmjit::Error jit_runtime::_release(void* ptr) noexcept
|
||||
{
|
||||
return asmjit::kErrorOk;
|
||||
}
|
||||
|
||||
u8* jit_runtime::alloc(std::size_t size, uint align, bool exec) noexcept
|
||||
{
|
||||
if (exec)
|
||||
{
|
||||
return add_jit_memory<s_code_pos, 0x0, utils::protection::wx>(size, align);
|
||||
}
|
||||
else
|
||||
{
|
||||
return add_jit_memory<s_data_pos, 0x40000000, utils::protection::rw>(size, align);
|
||||
}
|
||||
}
|
||||
|
||||
void jit_runtime::initialize()
|
||||
{
|
||||
if (!s_code_init.empty() || !s_data_init.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create code/data snapshot
|
||||
s_code_init.resize(s_code_pos);
|
||||
std::memcpy(s_code_init.data(), alloc(0, 0, true), s_code_pos);
|
||||
s_data_init.resize(s_data_pos);
|
||||
std::memcpy(s_data_init.data(), alloc(0, 0, false), s_data_pos);
|
||||
}
|
||||
|
||||
void jit_runtime::finalize() noexcept
|
||||
{
|
||||
// Reset JIT memory
|
||||
#ifdef CAN_OVERCOMMIT
|
||||
utils::memory_reset(get_jit_memory(), 0x80000000);
|
||||
utils::memory_protect(get_jit_memory(), 0x40000000, utils::protection::wx);
|
||||
#else
|
||||
utils::memory_decommit(get_jit_memory(), 0x80000000);
|
||||
#endif
|
||||
|
||||
s_code_pos = 0;
|
||||
s_data_pos = 0;
|
||||
|
||||
// Restore code/data snapshot
|
||||
std::memcpy(alloc(s_code_init.size(), 1, true), s_code_init.data(), s_code_init.size());
|
||||
std::memcpy(alloc(s_data_init.size(), 1, false), s_data_init.data(), s_data_init.size());
|
||||
}
|
||||
|
||||
asmjit::JitRuntime& asmjit::get_global_runtime()
|
||||
{
|
||||
// Magic static
|
||||
static asmjit::JitRuntime g_rt;
|
||||
return g_rt;
|
||||
}
|
||||
|
||||
void asmjit::build_transaction_enter(asmjit::X86Assembler& c, asmjit::Label fallback, const asmjit::X86Gp& ctr, uint less_than)
|
||||
{
|
||||
Label fall = c.newLabel();
|
||||
Label begin = c.newLabel();
|
||||
c.jmp(begin);
|
||||
c.bind(fall);
|
||||
|
||||
if (less_than < 65)
|
||||
{
|
||||
c.add(ctr, 1);
|
||||
c.test(x86::eax, _XABORT_RETRY);
|
||||
c.jz(fallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Count an attempt without RETRY flag as 65 normal attempts and continue
|
||||
c.not_(x86::eax);
|
||||
c.and_(x86::eax, _XABORT_RETRY);
|
||||
c.shl(x86::eax, 5);
|
||||
c.add(x86::eax, 1); // eax = RETRY ? 1 : 65
|
||||
c.add(ctr, x86::rax);
|
||||
}
|
||||
|
||||
c.cmp(ctr, less_than);
|
||||
c.jae(fallback);
|
||||
c.align(kAlignCode, 16);
|
||||
c.bind(begin);
|
||||
c.xbegin(fall);
|
||||
}
|
||||
|
||||
void asmjit::build_transaction_abort(asmjit::X86Assembler& c, unsigned char code)
|
||||
{
|
||||
c.db(0xc6);
|
||||
c.db(0xf8);
|
||||
c.db(code);
|
||||
}
|
||||
|
||||
#ifdef LLVM_AVAILABLE
|
||||
|
||||
#include <unordered_map>
|
||||
@@ -236,6 +7,13 @@ void asmjit::build_transaction_abort(asmjit::X86Assembler& c, unsigned char code
|
||||
#include <array>
|
||||
#include <deque>
|
||||
|
||||
#include "types.h"
|
||||
#include "StrFmt.h"
|
||||
#include "File.h"
|
||||
#include "Log.h"
|
||||
#include "mutex.h"
|
||||
#include "VirtualMemory.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#endif
|
||||
@@ -251,10 +29,10 @@ void asmjit::build_transaction_abort(asmjit::X86Assembler& c, unsigned char code
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "JIT.h"
|
||||
|
||||
// Memory manager mutex
|
||||
shared_mutex s_mutex;
|
||||
|
||||
@@ -266,14 +44,8 @@ static void* const s_memory = []() -> void*
|
||||
{
|
||||
llvm::InitializeNativeTarget();
|
||||
llvm::InitializeNativeTargetAsmPrinter();
|
||||
llvm::InitializeNativeTargetAsmParser();
|
||||
LLVMLinkInMCJIT();
|
||||
|
||||
#ifdef MAP_32BIT
|
||||
auto ptr = ::mmap(nullptr, s_memory_size, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_32BIT, -1, 0);
|
||||
if (ptr != MAP_FAILED)
|
||||
return ptr;
|
||||
#else
|
||||
for (u64 addr = 0x10000000; addr <= 0x80000000 - s_memory_size; addr += 0x1000000)
|
||||
{
|
||||
if (auto ptr = utils::memory_reserve(s_memory_size, (void*)addr))
|
||||
@@ -281,7 +53,6 @@ static void* const s_memory = []() -> void*
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return utils::memory_reserve(s_memory_size);
|
||||
}();
|
||||
@@ -291,8 +62,6 @@ static void* s_next = s_memory;
|
||||
#ifdef _WIN32
|
||||
static std::deque<std::vector<RUNTIME_FUNCTION>> s_unwater;
|
||||
static std::vector<std::vector<RUNTIME_FUNCTION>> s_unwind; // .pdata
|
||||
#else
|
||||
static std::deque<std::pair<u8*, std::size_t>> s_unfire;
|
||||
#endif
|
||||
|
||||
// Reset memory manager
|
||||
@@ -309,12 +78,7 @@ extern void jit_finalize()
|
||||
|
||||
s_unwind.clear();
|
||||
#else
|
||||
for (auto&& t : s_unfire)
|
||||
{
|
||||
llvm::RTDyldMemoryManager::deregisterEHFramesInProcess(t.first, t.second);
|
||||
}
|
||||
|
||||
s_unfire.clear();
|
||||
// TODO: unregister EH frames if necessary
|
||||
#endif
|
||||
|
||||
utils::memory_decommit(s_memory, s_memory_size);
|
||||
@@ -325,13 +89,13 @@ extern void jit_finalize()
|
||||
// Helper class
|
||||
struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
{
|
||||
std::unordered_map<std::string, u64>& m_link;
|
||||
std::unordered_map<std::string, std::uintptr_t>& m_link;
|
||||
|
||||
std::array<u8, 16>* m_tramps{};
|
||||
|
||||
u8* m_code_addr{}; // TODO
|
||||
|
||||
MemoryManager(std::unordered_map<std::string, u64>& table)
|
||||
MemoryManager(std::unordered_map<std::string, std::uintptr_t>& table)
|
||||
: m_link(table)
|
||||
{
|
||||
}
|
||||
@@ -365,7 +129,7 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
if ((u64)s_memory > 0x80000000 - s_memory_size ? (u64)addr - (u64)s_memory >= s_memory_size : addr >= 0x80000000)
|
||||
{
|
||||
// Lock memory manager
|
||||
std::lock_guard lock(s_mutex);
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Allocate memory for trampolines
|
||||
if (!m_tramps)
|
||||
@@ -398,10 +162,10 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
return {addr, llvm::JITSymbolFlags::Exported};
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
virtual u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
{
|
||||
// Lock memory manager
|
||||
std::lock_guard lock(s_mutex);
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Simple allocation
|
||||
const u64 next = ::align((u64)s_next + size, 4096);
|
||||
@@ -419,10 +183,10 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
return (u8*)std::exchange(s_next, (void*)next);
|
||||
}
|
||||
|
||||
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
virtual u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
{
|
||||
// Lock memory manager
|
||||
std::lock_guard lock(s_mutex);
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Simple allocation
|
||||
const u64 next = ::align((u64)s_next + size, 4096);
|
||||
@@ -435,6 +199,7 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
|
||||
if (!is_ro)
|
||||
{
|
||||
LOG_ERROR(GENERAL, "LLVM: Writeable data section not supported!");
|
||||
}
|
||||
|
||||
utils::memory_commit(s_next, size);
|
||||
@@ -443,10 +208,10 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
return (u8*)std::exchange(s_next, (void*)next);
|
||||
}
|
||||
|
||||
bool finalizeMemory(std::string* = nullptr) override
|
||||
virtual bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
// Lock memory manager
|
||||
std::lock_guard lock(s_mutex);
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// TODO: make only read-only sections read-only
|
||||
//#ifdef _WIN32
|
||||
@@ -460,11 +225,11 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
virtual void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Lock memory manager
|
||||
std::lock_guard lock(s_mutex);
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Use s_memory as a BASE, compute the difference
|
||||
const u64 unwind_diff = (u64)addr - (u64)s_memory;
|
||||
@@ -487,97 +252,16 @@ struct MemoryManager : llvm::RTDyldMemoryManager
|
||||
{
|
||||
s_unwind.emplace_back(std::move(pdata));
|
||||
}
|
||||
#else
|
||||
s_unfire.push_front(std::make_pair(addr, size));
|
||||
#endif
|
||||
|
||||
return RTDyldMemoryManager::registerEHFramesInProcess(addr, size);
|
||||
return RTDyldMemoryManager::registerEHFrames(addr, load_addr, size);
|
||||
}
|
||||
|
||||
void deregisterEHFrames() override
|
||||
virtual void deregisterEHFrames() override
|
||||
{
|
||||
}
|
||||
};
|
||||
LOG_ERROR(GENERAL, "deregisterEHFrames() called"); // Not expected
|
||||
|
||||
// Simple memory manager
|
||||
struct MemoryManager2 : llvm::RTDyldMemoryManager
|
||||
{
|
||||
MemoryManager2() = default;
|
||||
|
||||
~MemoryManager2() override
|
||||
{
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
{
|
||||
return jit_runtime::alloc(size, align, true);
|
||||
}
|
||||
|
||||
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
{
|
||||
return jit_runtime::alloc(size, align, false);
|
||||
}
|
||||
|
||||
bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
{
|
||||
#ifndef _WIN32
|
||||
RTDyldMemoryManager::registerEHFramesInProcess(addr, size);
|
||||
s_unfire.push_front(std::make_pair(addr, size));
|
||||
#endif
|
||||
}
|
||||
|
||||
void deregisterEHFrames() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Simple memory manager. I promise there will be no MemoryManager4.
|
||||
struct MemoryManager3 : llvm::RTDyldMemoryManager
|
||||
{
|
||||
std::vector<std::pair<u8*, std::size_t>> allocs;
|
||||
|
||||
MemoryManager3() = default;
|
||||
|
||||
~MemoryManager3() override
|
||||
{
|
||||
for (auto& a : allocs)
|
||||
{
|
||||
utils::memory_release(a.first, a.second);
|
||||
}
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
{
|
||||
u8* r = static_cast<u8*>(utils::memory_reserve(size));
|
||||
utils::memory_commit(r, size, utils::protection::wx);
|
||||
allocs.emplace_back(r, size);
|
||||
return r;
|
||||
}
|
||||
|
||||
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
{
|
||||
u8* r = static_cast<u8*>(utils::memory_reserve(size));
|
||||
utils::memory_commit(r, size);
|
||||
allocs.emplace_back(r, size);
|
||||
return r;
|
||||
}
|
||||
|
||||
bool finalizeMemory(std::string* = nullptr) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
{
|
||||
}
|
||||
|
||||
void deregisterEHFrames() override
|
||||
{
|
||||
return RTDyldMemoryManager::deregisterEHFrames();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -591,7 +275,7 @@ struct EventListener : llvm::JITEventListener
|
||||
{
|
||||
}
|
||||
|
||||
void notifyObjectLoaded(ObjectKey K, const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
|
||||
virtual void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
|
||||
{
|
||||
#ifdef _WIN32
|
||||
for (auto it = obj.section_begin(), end = obj.section_end(); it != end; ++it)
|
||||
@@ -619,7 +303,7 @@ struct EventListener : llvm::JITEventListener
|
||||
}
|
||||
|
||||
// Lock memory manager
|
||||
std::lock_guard lock(s_mutex);
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
// Use s_memory as a BASE, compute the difference
|
||||
const u64 code_diff = (u64)m_mem.m_code_addr - (u64)s_memory;
|
||||
@@ -656,107 +340,46 @@ public:
|
||||
std::string name = m_path;
|
||||
name.append(module->getName());
|
||||
fs::file(name, fs::rewrite).write(obj.getBufferStart(), obj.getBufferSize());
|
||||
LOG_NOTICE(GENERAL, "LLVM: Created module: %s", module->getName().data());
|
||||
}
|
||||
|
||||
static std::unique_ptr<llvm::MemoryBuffer> load(const std::string& path)
|
||||
{
|
||||
if (fs::file cached{path, fs::read})
|
||||
{
|
||||
auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(cached.size());
|
||||
cached.read(buf->getBufferStart(), buf->getBufferSize());
|
||||
return buf;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Created module: %s", module->getName().data());
|
||||
}
|
||||
|
||||
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* module) override
|
||||
{
|
||||
std::string path = m_path;
|
||||
path.append(module->getName());
|
||||
std::string name = m_path;
|
||||
name.append(module->getName());
|
||||
|
||||
if (auto buf = load(path))
|
||||
if (fs::file cached{name, fs::read})
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "LLVM: Loaded module: %s", module->getName().data());
|
||||
auto buf = llvm::MemoryBuffer::getNewUninitMemBuffer(cached.size());
|
||||
cached.read(const_cast<char*>(buf->getBufferStart()), buf->getBufferSize());
|
||||
LOG_SUCCESS(GENERAL, "LLVM: Loaded module: %s", module->getName().data());
|
||||
return buf;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::string jit_compiler::cpu(const std::string& _cpu)
|
||||
jit_compiler::jit_compiler(const std::unordered_map<std::string, std::uintptr_t>& _link, std::string _cpu)
|
||||
: m_link(std::move(_link))
|
||||
, m_cpu(std::move(_cpu))
|
||||
{
|
||||
std::string m_cpu = _cpu;
|
||||
|
||||
if (m_cpu.empty())
|
||||
{
|
||||
m_cpu = llvm::sys::getHostCPUName();
|
||||
|
||||
if (m_cpu == "sandybridge" ||
|
||||
m_cpu == "ivybridge" ||
|
||||
m_cpu == "haswell" ||
|
||||
m_cpu == "broadwell" ||
|
||||
m_cpu == "skylake" ||
|
||||
m_cpu == "skylake-avx512" ||
|
||||
m_cpu == "cascadelake" ||
|
||||
m_cpu == "cannonlake" ||
|
||||
m_cpu == "icelake" ||
|
||||
m_cpu == "icelake-client" ||
|
||||
m_cpu == "icelake-server")
|
||||
{
|
||||
// Downgrade if AVX is not supported by some chips
|
||||
if (!utils::has_avx())
|
||||
{
|
||||
m_cpu = "nehalem";
|
||||
}
|
||||
}
|
||||
|
||||
if (m_cpu == "skylake-avx512" ||
|
||||
m_cpu == "cascadelake" ||
|
||||
m_cpu == "cannonlake" ||
|
||||
m_cpu == "icelake" ||
|
||||
m_cpu == "icelake-client" ||
|
||||
m_cpu == "icelake-server")
|
||||
{
|
||||
// Downgrade if AVX-512 is disabled or not supported
|
||||
if (!utils::has_512())
|
||||
{
|
||||
m_cpu = "skylake";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_cpu;
|
||||
}
|
||||
|
||||
jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, const std::string& _cpu, u32 flags)
|
||||
: m_link(_link)
|
||||
, m_cpu(cpu(_cpu))
|
||||
{
|
||||
std::string result;
|
||||
|
||||
if (m_link.empty())
|
||||
{
|
||||
std::unique_ptr<llvm::RTDyldMemoryManager> mem;
|
||||
|
||||
if (flags & 0x1)
|
||||
{
|
||||
mem = std::make_unique<MemoryManager3>();
|
||||
}
|
||||
else
|
||||
{
|
||||
mem = std::make_unique<MemoryManager2>();
|
||||
}
|
||||
|
||||
// Auxiliary JIT (does not use custom memory manager, only writes the objects)
|
||||
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("null_", m_context))
|
||||
.setErrorStr(&result)
|
||||
.setEngineKind(llvm::EngineKind::JIT)
|
||||
.setMCJITMemoryManager(std::move(mem))
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setCodeModel(flags & 0x2 ? llvm::CodeModel::Large : llvm::CodeModel::Small)
|
||||
.setCodeModel(llvm::CodeModel::Small)
|
||||
.setMCPU(m_cpu)
|
||||
.create());
|
||||
}
|
||||
@@ -768,10 +391,9 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, co
|
||||
|
||||
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("null", m_context))
|
||||
.setErrorStr(&result)
|
||||
.setEngineKind(llvm::EngineKind::JIT)
|
||||
.setMCJITMemoryManager(std::move(mem))
|
||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||
.setCodeModel(flags & 0x2 ? llvm::CodeModel::Large : llvm::CodeModel::Small)
|
||||
.setCodeModel(llvm::CodeModel::Small)
|
||||
.setMCPU(m_cpu)
|
||||
.create());
|
||||
|
||||
@@ -808,24 +430,6 @@ void jit_compiler::add(std::unique_ptr<llvm::Module> module, const std::string&
|
||||
}
|
||||
}
|
||||
|
||||
void jit_compiler::add(std::unique_ptr<llvm::Module> module)
|
||||
{
|
||||
const auto ptr = module.get();
|
||||
m_engine->addModule(std::move(module));
|
||||
m_engine->generateCodeForModule(ptr);
|
||||
|
||||
for (auto& func : ptr->functions())
|
||||
{
|
||||
// Delete IR to lower memory consumption
|
||||
func.deleteBody();
|
||||
}
|
||||
}
|
||||
|
||||
void jit_compiler::add(const std::string& path)
|
||||
{
|
||||
m_engine->addObjectFile(std::move(llvm::object::ObjectFile::createObjectFile(*ObjectCache::load(path)).get()));
|
||||
}
|
||||
|
||||
void jit_compiler::fin()
|
||||
{
|
||||
m_engine->finalizeObject();
|
||||
@@ -836,4 +440,33 @@ u64 jit_compiler::get(const std::string& name)
|
||||
return m_engine->getGlobalValueAddress(name);
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, u64> jit_compiler::add(std::unordered_map<std::string, std::string> data)
|
||||
{
|
||||
// Lock memory manager
|
||||
writer_lock lock(s_mutex);
|
||||
|
||||
std::unordered_map<std::string, u64> result;
|
||||
|
||||
std::size_t size = 0;
|
||||
|
||||
for (auto&& pair : data)
|
||||
{
|
||||
size += ::align(pair.second.size(), 16);
|
||||
}
|
||||
|
||||
utils::memory_commit(s_next, size, utils::protection::wx);
|
||||
std::memset(s_next, 0xc3, ::align(size, 4096));
|
||||
|
||||
for (auto&& pair : data)
|
||||
{
|
||||
std::memcpy(s_next, pair.second.data(), pair.second.size());
|
||||
result.emplace(pair.first, (u64)s_next);
|
||||
s_next = (void*)::align((u64)s_next + pair.second.size(), 16);
|
||||
}
|
||||
|
||||
s_next = (void*)::align((u64)s_next, 4096);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+9
-102
@@ -1,97 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#define ASMJIT_EMBED
|
||||
#define ASMJIT_DEBUG
|
||||
|
||||
#include <asmjit/asmjit.h>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
|
||||
enum class jit_class
|
||||
{
|
||||
ppu_code,
|
||||
ppu_data,
|
||||
spu_code,
|
||||
spu_data,
|
||||
};
|
||||
|
||||
// ASMJIT runtime for emitting code in a single 2G region
|
||||
struct jit_runtime final : asmjit::HostRuntime
|
||||
{
|
||||
jit_runtime();
|
||||
~jit_runtime() override;
|
||||
|
||||
// Allocate executable memory
|
||||
asmjit::Error _add(void** dst, asmjit::CodeHolder* code) noexcept override;
|
||||
|
||||
// Do nothing (deallocation is delayed)
|
||||
asmjit::Error _release(void* p) noexcept override;
|
||||
|
||||
// Allocate memory
|
||||
static u8* alloc(std::size_t size, uint align, bool exec = true) noexcept;
|
||||
|
||||
// Should be called at least once after global initialization
|
||||
static void initialize();
|
||||
|
||||
// Deallocate all memory
|
||||
static void finalize() noexcept;
|
||||
};
|
||||
|
||||
namespace asmjit
|
||||
{
|
||||
// Should only be used to build global functions
|
||||
asmjit::JitRuntime& get_global_runtime();
|
||||
|
||||
// Emit xbegin and adjacent loop, return label at xbegin
|
||||
void build_transaction_enter(X86Assembler& c, Label fallback, const X86Gp& ctr, uint less_than);
|
||||
|
||||
// Emit xabort
|
||||
void build_transaction_abort(X86Assembler& c, unsigned char code);
|
||||
}
|
||||
|
||||
// Build runtime function with asmjit::X86Assembler
|
||||
template <typename FT, typename F>
|
||||
FT build_function_asm(F&& builder)
|
||||
{
|
||||
using namespace asmjit;
|
||||
|
||||
auto& rt = get_global_runtime();
|
||||
|
||||
CodeHolder code;
|
||||
code.init(rt.getCodeInfo());
|
||||
code._globalHints = asmjit::CodeEmitter::kHintOptimizedAlign;
|
||||
|
||||
std::array<X86Gp, 4> args;
|
||||
#ifdef _WIN32
|
||||
args[0] = x86::rcx;
|
||||
args[1] = x86::rdx;
|
||||
args[2] = x86::r8;
|
||||
args[3] = x86::r9;
|
||||
#else
|
||||
args[0] = x86::rdi;
|
||||
args[1] = x86::rsi;
|
||||
args[2] = x86::rdx;
|
||||
args[3] = x86::rcx;
|
||||
#endif
|
||||
|
||||
X86Assembler compiler(&code);
|
||||
builder(std::ref(compiler), args);
|
||||
|
||||
FT result;
|
||||
|
||||
if (rt.add(&result, &code))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef LLVM_AVAILABLE
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "types.h"
|
||||
@@ -128,7 +40,7 @@ class jit_compiler final
|
||||
std::string m_cpu;
|
||||
|
||||
public:
|
||||
jit_compiler(const std::unordered_map<std::string, u64>& _link, const std::string& _cpu, u32 flags = 0);
|
||||
jit_compiler(const std::unordered_map<std::string, u64>& _link, std::string _cpu);
|
||||
~jit_compiler();
|
||||
|
||||
// Get LLVM context
|
||||
@@ -137,28 +49,23 @@ public:
|
||||
return m_context;
|
||||
}
|
||||
|
||||
auto& get_engine() const
|
||||
{
|
||||
return *m_engine;
|
||||
}
|
||||
|
||||
// Add module (path to obj cache dir)
|
||||
// Add module
|
||||
void add(std::unique_ptr<llvm::Module> module, const std::string& path);
|
||||
|
||||
// Add module (not cached)
|
||||
void add(std::unique_ptr<llvm::Module> module);
|
||||
|
||||
// Add object (path to obj file)
|
||||
void add(const std::string& path);
|
||||
|
||||
// Finalize
|
||||
void fin();
|
||||
|
||||
// Get compiled function address
|
||||
u64 get(const std::string& name);
|
||||
|
||||
// Add functions directly to the memory manager (name -> code)
|
||||
static std::unordered_map<std::string, u64> add(std::unordered_map<std::string, std::string>);
|
||||
|
||||
// Get CPU info
|
||||
static std::string cpu(const std::string& _cpu);
|
||||
const std::string& cpu() const
|
||||
{
|
||||
return m_cpu;
|
||||
}
|
||||
|
||||
// Check JIT purpose
|
||||
bool is_primary() const
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,265 +0,0 @@
|
||||
/*
|
||||
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
||||
* https://github.com/corporateshark/LUrlParser
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "LUrlParser.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <stdlib.h>
|
||||
|
||||
// check if the scheme name is valid
|
||||
static bool IsSchemeValid( const std::string& SchemeName )
|
||||
{
|
||||
for ( auto c : SchemeName )
|
||||
{
|
||||
if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LUrlParser::clParseURL::GetPort( int* OutPort ) const
|
||||
{
|
||||
if ( !IsValid() ) { return false; }
|
||||
|
||||
int Port = atoi( m_Port.c_str() );
|
||||
|
||||
if ( Port <= 0 || Port > 65535 ) { return false; }
|
||||
|
||||
if ( OutPort ) { *OutPort = Port; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// based on RFC 1738 and RFC 3986
|
||||
LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL )
|
||||
{
|
||||
LUrlParser::clParseURL Result;
|
||||
|
||||
const char* CurrentString = URL.c_str();
|
||||
|
||||
/*
|
||||
* <scheme>:<scheme-specific-part>
|
||||
* <scheme> := [a-z\+\-\.]+
|
||||
* For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names
|
||||
*/
|
||||
|
||||
// try to read scheme
|
||||
{
|
||||
const char* LocalString = strchr( CurrentString, ':' );
|
||||
|
||||
if ( !LocalString )
|
||||
{
|
||||
return clParseURL( LUrlParserError_NoUrlCharacter );
|
||||
}
|
||||
|
||||
// save the scheme name
|
||||
Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
if ( !IsSchemeValid( Result.m_Scheme ) )
|
||||
{
|
||||
return clParseURL( LUrlParserError_InvalidSchemeName );
|
||||
}
|
||||
|
||||
// scheme should be lowercase
|
||||
std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower );
|
||||
|
||||
// skip ':'
|
||||
CurrentString = LocalString+1;
|
||||
}
|
||||
|
||||
/*
|
||||
* //<user>:<password>@<host>:<port>/<url-path>
|
||||
* any ":", "@" and "/" must be normalized
|
||||
*/
|
||||
|
||||
// skip "//"
|
||||
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
|
||||
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
|
||||
|
||||
// check if the user name and password are specified
|
||||
bool bHasUserName = false;
|
||||
|
||||
const char* LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString )
|
||||
{
|
||||
if ( *LocalString == '@' )
|
||||
{
|
||||
// user name and password are specified
|
||||
bHasUserName = true;
|
||||
break;
|
||||
}
|
||||
else if ( *LocalString == '/' )
|
||||
{
|
||||
// end of <host>:<port> specification
|
||||
bHasUserName = false;
|
||||
break;
|
||||
}
|
||||
|
||||
LocalString++;
|
||||
}
|
||||
|
||||
// user name and password
|
||||
LocalString = CurrentString;
|
||||
|
||||
if ( bHasUserName )
|
||||
{
|
||||
// read user name
|
||||
while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++;
|
||||
|
||||
Result.m_UserName = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
// proceed with the current pointer
|
||||
CurrentString = LocalString;
|
||||
|
||||
if ( *CurrentString == ':' )
|
||||
{
|
||||
// skip ':'
|
||||
CurrentString++;
|
||||
|
||||
// read password
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString && *LocalString != '@' ) LocalString++;
|
||||
|
||||
Result.m_Password = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
}
|
||||
|
||||
// skip '@'
|
||||
if ( *CurrentString != '@' )
|
||||
{
|
||||
return clParseURL( LUrlParserError_NoAtSign );
|
||||
}
|
||||
|
||||
CurrentString++;
|
||||
}
|
||||
|
||||
bool bHasBracket = ( *CurrentString == '[' );
|
||||
|
||||
// go ahead, read the host name
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString )
|
||||
{
|
||||
if ( bHasBracket && *LocalString == ']' )
|
||||
{
|
||||
// end of IPv6 address
|
||||
LocalString++;
|
||||
break;
|
||||
}
|
||||
else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) )
|
||||
{
|
||||
// port number is specified
|
||||
break;
|
||||
}
|
||||
|
||||
LocalString++;
|
||||
}
|
||||
|
||||
Result.m_Host = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
|
||||
// is port number specified?
|
||||
if ( *CurrentString == ':' )
|
||||
{
|
||||
CurrentString++;
|
||||
|
||||
// read port number
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString && *LocalString != '/' ) LocalString++;
|
||||
|
||||
Result.m_Port = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
}
|
||||
|
||||
// end of string
|
||||
if ( !*CurrentString )
|
||||
{
|
||||
Result.m_ErrorCode = LUrlParserError_Ok;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// skip '/'
|
||||
if ( *CurrentString != '/' )
|
||||
{
|
||||
return clParseURL( LUrlParserError_NoSlash );
|
||||
}
|
||||
|
||||
CurrentString++;
|
||||
|
||||
// parse the path
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++;
|
||||
|
||||
Result.m_Path = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
|
||||
// check for query
|
||||
if ( *CurrentString == '?' )
|
||||
{
|
||||
// skip '?'
|
||||
CurrentString++;
|
||||
|
||||
// read query
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString && *LocalString != '#' ) LocalString++;
|
||||
|
||||
Result.m_Query = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
}
|
||||
|
||||
// check for fragment
|
||||
if ( *CurrentString == '#' )
|
||||
{
|
||||
// skip '#'
|
||||
CurrentString++;
|
||||
|
||||
// read fragment
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString ) LocalString++;
|
||||
|
||||
Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
}
|
||||
|
||||
Result.m_ErrorCode = LUrlParserError_Ok;
|
||||
|
||||
return Result;
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
||||
* https://github.com/corporateshark/LUrlParser
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace LUrlParser
|
||||
{
|
||||
enum LUrlParserError
|
||||
{
|
||||
LUrlParserError_Ok = 0,
|
||||
LUrlParserError_Uninitialized = 1,
|
||||
LUrlParserError_NoUrlCharacter = 2,
|
||||
LUrlParserError_InvalidSchemeName = 3,
|
||||
LUrlParserError_NoDoubleSlash = 4,
|
||||
LUrlParserError_NoAtSign = 5,
|
||||
LUrlParserError_UnexpectedEndOfLine = 6,
|
||||
LUrlParserError_NoSlash = 7,
|
||||
};
|
||||
|
||||
class clParseURL
|
||||
{
|
||||
public:
|
||||
LUrlParserError m_ErrorCode;
|
||||
std::string m_Scheme;
|
||||
std::string m_Host;
|
||||
std::string m_Port;
|
||||
std::string m_Path;
|
||||
std::string m_Query;
|
||||
std::string m_Fragment;
|
||||
std::string m_UserName;
|
||||
std::string m_Password;
|
||||
|
||||
clParseURL()
|
||||
: m_ErrorCode( LUrlParserError_Uninitialized )
|
||||
{}
|
||||
|
||||
/// return 'true' if the parsing was successful
|
||||
bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; }
|
||||
|
||||
/// helper to convert the port number to int, return 'true' if the port is valid (within the 0..65535 range)
|
||||
bool GetPort( int* OutPort ) const;
|
||||
|
||||
/// parse the URL
|
||||
static clParseURL ParseURL( const std::string& URL );
|
||||
|
||||
private:
|
||||
explicit clParseURL( LUrlParserError ErrorCode )
|
||||
: m_ErrorCode( ErrorCode )
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace LUrlParser
|
||||
+55
-427
@@ -3,29 +3,16 @@
|
||||
#include "StrFmt.h"
|
||||
#include "sema.h"
|
||||
|
||||
#include "Utilities/sysinfo.h"
|
||||
#include "Utilities/Thread.h"
|
||||
#include "rpcs3_version.h"
|
||||
#include <cstring>
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <chrono>
|
||||
#endif
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
static std::string empty_string()
|
||||
{
|
||||
return {};
|
||||
@@ -49,7 +36,6 @@ void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
|
||||
case logs::level::warning: return "Warning";
|
||||
case logs::level::notice: return "Notice";
|
||||
case logs::level::trace: return "Trace";
|
||||
case logs::level::_uninit: return unknown;
|
||||
}
|
||||
|
||||
return unknown;
|
||||
@@ -58,86 +44,38 @@ void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
|
||||
|
||||
namespace logs
|
||||
{
|
||||
// Memory-mapped buffer size
|
||||
constexpr u64 s_log_size = 32 * 1024 * 1024;
|
||||
|
||||
class file_writer
|
||||
{
|
||||
// Could be memory-mapped file
|
||||
fs::file m_file;
|
||||
std::string m_name;
|
||||
std::thread m_writer;
|
||||
fs::file m_fout;
|
||||
fs::file m_fout2;
|
||||
u64 m_max_size;
|
||||
|
||||
#ifdef _WIN32
|
||||
::HANDLE m_fmap;
|
||||
#endif
|
||||
uchar* m_fptr{};
|
||||
z_stream m_zs{};
|
||||
shared_mutex m_m;
|
||||
|
||||
alignas(128) atomic_t<u64> m_buf{0}; // MSB (40 bit): push begin, LSB (24 bis): push size
|
||||
alignas(128) atomic_t<u64> m_out{0}; // Amount of bytes written to file
|
||||
|
||||
uchar m_zout[65536];
|
||||
|
||||
// Write buffered logs immediately
|
||||
bool flush(u64 bufv);
|
||||
|
||||
public:
|
||||
file_writer(const std::string& name);
|
||||
|
||||
virtual ~file_writer();
|
||||
virtual ~file_writer() = default;
|
||||
|
||||
// Append raw data
|
||||
void log(logs::level sev, const char* text, std::size_t size);
|
||||
};
|
||||
|
||||
struct channel_info
|
||||
{
|
||||
channel* pointer = nullptr;
|
||||
level enabled = level::notice;
|
||||
|
||||
void set_level(level value)
|
||||
{
|
||||
enabled = value;
|
||||
|
||||
if (pointer)
|
||||
{
|
||||
pointer->enabled = value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct stored_message
|
||||
{
|
||||
message m;
|
||||
u64 stamp;
|
||||
std::string prefix;
|
||||
std::string text;
|
||||
void log(const char* text, std::size_t size);
|
||||
};
|
||||
|
||||
struct file_listener : public file_writer, public listener
|
||||
{
|
||||
file_listener(const std::string& name);
|
||||
|
||||
virtual ~file_listener() = default;
|
||||
file_listener(const std::string& name)
|
||||
: file_writer(name)
|
||||
, listener()
|
||||
{
|
||||
const std::string& start = fmt::format("\xEF\xBB\xBF" "RPCS3 v%s\n", rpcs3::version.to_string());
|
||||
file_writer::log(start.data(), start.size());
|
||||
}
|
||||
|
||||
// Encode level, current thread name, channel name and write log message
|
||||
virtual void log(u64 stamp, const message& msg, const std::string& prefix, const std::string& text) override;
|
||||
|
||||
// Channel registry
|
||||
std::unordered_map<std::string, channel_info> channels;
|
||||
|
||||
// Messages for delayed listener initialization
|
||||
std::vector<stored_message> messages;
|
||||
};
|
||||
|
||||
static file_listener* get_logger()
|
||||
{
|
||||
// Use magic static
|
||||
static file_listener logger("RPCS3");
|
||||
static file_listener logger("RPCS3.log");
|
||||
return &logger;
|
||||
}
|
||||
|
||||
@@ -181,18 +119,35 @@ namespace logs
|
||||
channel HLE("HLE");
|
||||
channel PPU("PPU");
|
||||
channel SPU("SPU");
|
||||
channel ARMv7("ARMv7");
|
||||
|
||||
struct channel_info
|
||||
{
|
||||
channel* pointer = nullptr;
|
||||
level enabled = level::notice;
|
||||
|
||||
void set_level(level value)
|
||||
{
|
||||
enabled = value;
|
||||
|
||||
if (pointer)
|
||||
{
|
||||
pointer->enabled = value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Channel registry mutex
|
||||
shared_mutex g_mutex;
|
||||
semaphore<> g_mutex;
|
||||
|
||||
// Must be set to true in main()
|
||||
atomic_t<bool> g_init{false};
|
||||
// Channel registry
|
||||
std::unordered_map<std::string, channel_info> g_channels;
|
||||
|
||||
void reset()
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
semaphore_lock lock(g_mutex);
|
||||
|
||||
for (auto&& pair : get_logger()->channels)
|
||||
for (auto&& pair : g_channels)
|
||||
{
|
||||
pair.second.set_level(level::notice);
|
||||
}
|
||||
@@ -200,20 +155,9 @@ namespace logs
|
||||
|
||||
void set_level(const std::string& ch_name, level value)
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
semaphore_lock lock(g_mutex);
|
||||
|
||||
get_logger()->channels[ch_name].set_level(value);
|
||||
}
|
||||
|
||||
// Must be called in main() to stop accumulating messages in g_messages
|
||||
void set_init()
|
||||
{
|
||||
if (!g_init)
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
get_logger()->messages.clear();
|
||||
g_init = true;
|
||||
}
|
||||
g_channels[ch_name].set_level(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,22 +170,14 @@ void logs::listener::add(logs::listener* _new)
|
||||
// Get first (main) listener
|
||||
listener* lis = get_logger();
|
||||
|
||||
std::lock_guard lock(g_mutex);
|
||||
|
||||
// Install new listener at the end of linked list
|
||||
while (lis->m_next || !lis->m_next.compare_and_swap_test(nullptr, _new))
|
||||
{
|
||||
lis = lis->m_next;
|
||||
}
|
||||
|
||||
// Send initial messages
|
||||
for (const auto& msg : get_logger()->messages)
|
||||
{
|
||||
_new->log(msg.stamp, msg.m, msg.prefix, msg.text);
|
||||
}
|
||||
}
|
||||
|
||||
void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) const
|
||||
void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u64* args)
|
||||
{
|
||||
// Get timestamp
|
||||
const u64 stamp = get_stamp();
|
||||
@@ -249,9 +185,9 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) co
|
||||
// Register channel
|
||||
if (ch->enabled == level::_uninit)
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
semaphore_lock lock(g_mutex);
|
||||
|
||||
auto& info = get_logger()->channels[ch->name];
|
||||
auto& info = g_channels[ch->name];
|
||||
|
||||
if (info.pointer && info.pointer != ch)
|
||||
{
|
||||
@@ -270,45 +206,14 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) co
|
||||
}
|
||||
}
|
||||
|
||||
// Get text, extract va_args
|
||||
thread_local std::string text;
|
||||
thread_local std::vector<u64> args;
|
||||
|
||||
std::size_t args_count = 0;
|
||||
for (auto v = sup; v->fmt_string; v++)
|
||||
args_count++;
|
||||
|
||||
text.clear();
|
||||
args.resize(args_count);
|
||||
|
||||
va_list c_args;
|
||||
va_start(c_args, sup);
|
||||
for (u64& arg : args)
|
||||
arg = va_arg(c_args, u64);
|
||||
va_end(c_args);
|
||||
fmt::raw_append(text, fmt, sup, args.data());
|
||||
// Get text
|
||||
std::string text;
|
||||
fmt::raw_append(text, fmt, sup, args);
|
||||
std::string prefix = g_tls_log_prefix();
|
||||
|
||||
// Get first (main) listener
|
||||
listener* lis = get_logger();
|
||||
|
||||
if (!g_init)
|
||||
{
|
||||
std::lock_guard lock(g_mutex);
|
||||
|
||||
if (!g_init)
|
||||
{
|
||||
while (lis)
|
||||
{
|
||||
lis->log(stamp, *this, prefix, text);
|
||||
lis = lis->m_next;
|
||||
}
|
||||
|
||||
// Store message additionally
|
||||
get_logger()->messages.emplace_back(stored_message{*this, stamp, std::move(prefix), text});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Send message to all listeners
|
||||
while (lis)
|
||||
{
|
||||
@@ -320,304 +225,28 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, ...) co
|
||||
[[noreturn]] extern void catch_all_exceptions();
|
||||
|
||||
logs::file_writer::file_writer(const std::string& name)
|
||||
: m_name(name)
|
||||
{
|
||||
const std::string log_name = fs::get_cache_dir() + name + ".log";
|
||||
const std::string buf_name = fs::get_cache_dir() + name + ".buf";
|
||||
|
||||
try
|
||||
{
|
||||
if (!m_file.open(buf_name, fs::read + fs::rewrite + fs::lock))
|
||||
if (!m_file.open(fs::get_config_dir() + name, fs::rewrite + fs::append))
|
||||
{
|
||||
if (fs::g_tls_error == fs::error::acces)
|
||||
{
|
||||
if (fs::exists(buf_name))
|
||||
{
|
||||
fmt::throw_exception("Another instance of %s is running. Close it or kill its process, if necessary.", name);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Cannot create %s.log (access denied)."
|
||||
#ifdef _WIN32
|
||||
"\nNote that %s cannot be installed in Program Files or similar directory with limited permissions."
|
||||
#else
|
||||
"\nPlease, check %s permissions in '~/.config/'."
|
||||
#endif
|
||||
, name, name);
|
||||
}
|
||||
}
|
||||
|
||||
fmt::throw_exception("Cannot create %s.log (error %s)", name, fs::g_tls_error);
|
||||
fmt::throw_exception("Can't create log file %s (error %s)", name, fs::g_tls_error);
|
||||
}
|
||||
|
||||
// Check free space
|
||||
fs::device_stat stats{};
|
||||
if (!fs::statfs(fs::get_cache_dir(), stats) || stats.avail_free < s_log_size * 8)
|
||||
{
|
||||
fmt::throw_exception("Not enough free space (%f KB)", stats.avail_free / 1000000.);
|
||||
}
|
||||
|
||||
// Limit log size to ~25% of free space
|
||||
m_max_size = stats.avail_free / 4;
|
||||
|
||||
// Initialize memory mapped file
|
||||
#ifdef _WIN32
|
||||
m_fmap = CreateFileMappingW(m_file.get_handle(), 0, PAGE_READWRITE, s_log_size >> 32, s_log_size & 0xffffffff, 0);
|
||||
m_fptr = m_fmap ? (uchar*)MapViewOfFile(m_fmap, FILE_MAP_WRITE, 0, 0, 0) : nullptr;
|
||||
#else
|
||||
m_file.trunc(s_log_size);
|
||||
m_fptr = (uchar*)::mmap(0, s_log_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_file.get_handle(), 0);
|
||||
#endif
|
||||
|
||||
verify(name.c_str()), m_fptr;
|
||||
|
||||
// Rotate backups (TODO)
|
||||
fs::remove_file(fs::get_cache_dir() + name + "1.log.gz");
|
||||
fs::create_dir(fs::get_cache_dir() + "old_logs");
|
||||
fs::rename(fs::get_cache_dir() + m_name + ".log.gz", fs::get_cache_dir() + "old_logs/" + m_name + ".log.gz", true);
|
||||
|
||||
// Actual log file (allowed to fail)
|
||||
m_fout.open(log_name, fs::rewrite);
|
||||
|
||||
// Compressed log, make it inaccessible (foolproof)
|
||||
if (!m_fout2.open(log_name + ".gz", fs::rewrite + fs::unread) || deflateInit2(&m_zs, 9, Z_DEFLATED, 16 + 15, 9, Z_DEFAULT_STRATEGY) != Z_OK)
|
||||
{
|
||||
m_fout2.close();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Autodelete compressed log file
|
||||
m_fout2.set_delete();
|
||||
#endif
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::thread([text = std::string{e.what()}]{ report_fatal_error(text); }).detach();
|
||||
return;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::thread([]{ report_fatal_error("Unknown error" HERE); }).detach();
|
||||
return;
|
||||
}
|
||||
|
||||
m_writer = std::thread([this]()
|
||||
{
|
||||
thread_ctrl::set_native_priority(-1);
|
||||
|
||||
while (true)
|
||||
{
|
||||
const u64 bufv = m_buf;
|
||||
|
||||
if (bufv & 0xffffff)
|
||||
{
|
||||
// Wait if threads are writing logs
|
||||
std::this_thread::yield();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!flush(bufv))
|
||||
{
|
||||
if (m_out == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
logs::file_writer::~file_writer()
|
||||
{
|
||||
if (!m_fptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop writer thread
|
||||
while (m_out << 24 < m_buf)
|
||||
{
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
m_out = -1;
|
||||
m_writer.join();
|
||||
|
||||
if (m_fout2)
|
||||
{
|
||||
m_zs.avail_in = 0;
|
||||
m_zs.next_in = nullptr;
|
||||
|
||||
do
|
||||
{
|
||||
m_zs.avail_out = sizeof(m_zout);
|
||||
m_zs.next_out = m_zout;
|
||||
|
||||
if (deflate(&m_zs, Z_FINISH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (m_zs.avail_out == 0);
|
||||
|
||||
deflateEnd(&m_zs);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Cancel compressed log file autodeletion
|
||||
m_fout2.set_delete(false);
|
||||
|
||||
UnmapViewOfFile(m_fptr);
|
||||
CloseHandle(m_fmap);
|
||||
#else
|
||||
// Restore compressed log file permissions
|
||||
::fchmod(m_fout2.get_handle(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
::munmap(m_fptr, s_log_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool logs::file_writer::flush(u64 bufv)
|
||||
{
|
||||
std::lock_guard lock(m_m);
|
||||
|
||||
const u64 st = +m_out;
|
||||
const u64 end = std::min<u64>((st + s_log_size) & ~(s_log_size - 1), bufv >> 24);
|
||||
|
||||
if (end > st)
|
||||
{
|
||||
// Avoid writing too big fragments
|
||||
const u64 size = std::min<u64>(end - st, sizeof(m_zout) / 2);
|
||||
|
||||
// Write uncompressed
|
||||
if (m_fout && st < m_max_size && m_fout.write(m_fptr + st % s_log_size, size) != size)
|
||||
{
|
||||
m_fout.close();
|
||||
}
|
||||
|
||||
// Write compressed
|
||||
if (m_fout2 && st < m_max_size)
|
||||
{
|
||||
m_zs.avail_in = size;
|
||||
m_zs.next_in = m_fptr + st % s_log_size;
|
||||
|
||||
do
|
||||
{
|
||||
m_zs.avail_out = sizeof(m_zout);
|
||||
m_zs.next_out = m_zout;
|
||||
|
||||
if (deflate(&m_zs, Z_NO_FLUSH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out)
|
||||
{
|
||||
deflateEnd(&m_zs);
|
||||
m_fout2.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (m_zs.avail_out == 0);
|
||||
}
|
||||
|
||||
m_out += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void logs::file_writer::log(logs::level sev, const char* text, std::size_t size)
|
||||
{
|
||||
if (!m_fptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: write bigger fragment directly in blocking manner
|
||||
while (size && size <= 0xffffff)
|
||||
{
|
||||
u64 bufv;
|
||||
|
||||
const auto pos = m_buf.atomic_op([&](u64& v) -> uchar*
|
||||
{
|
||||
const u64 v1 = v >> 24;
|
||||
const u64 v2 = v & 0xffffff;
|
||||
|
||||
if (UNLIKELY(v2 + size > 0xffffff || v1 + v2 + size >= m_out + s_log_size))
|
||||
{
|
||||
bufv = v;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
v += size;
|
||||
return m_fptr + (v1 + v2) % s_log_size;
|
||||
});
|
||||
|
||||
if (UNLIKELY(!pos))
|
||||
{
|
||||
if ((bufv & 0xffffff) + size > 0xffffff || bufv & 0xffffff)
|
||||
{
|
||||
// Concurrency limit reached
|
||||
std::this_thread::yield();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Queue is full, need to write out
|
||||
flush(bufv);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pos + size > m_fptr + s_log_size)
|
||||
{
|
||||
const auto frag = m_fptr + s_log_size - pos;
|
||||
std::memcpy(pos, text, frag);
|
||||
std::memcpy(m_fptr, text + frag, size - frag);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy(pos, text, size);
|
||||
}
|
||||
|
||||
m_buf += (u64{size} << 24) - size;
|
||||
break;
|
||||
catch_all_exceptions();
|
||||
}
|
||||
}
|
||||
|
||||
logs::file_listener::file_listener(const std::string& name)
|
||||
: file_writer(name)
|
||||
, listener()
|
||||
void logs::file_writer::log(const char* text, std::size_t size)
|
||||
{
|
||||
// Write UTF-8 BOM
|
||||
file_writer::log(logs::level::always, "\xEF\xBB\xBF", 3);
|
||||
|
||||
const std::string firmware_version = utils::get_firmware_version();
|
||||
const std::string firmware_string = firmware_version.empty() ? "" : (" | Firmware version: " + firmware_version);
|
||||
|
||||
// Write initial message
|
||||
stored_message ver;
|
||||
ver.m.ch = nullptr;
|
||||
ver.m.sev = level::always;
|
||||
ver.stamp = 0;
|
||||
ver.text = fmt::format("RPCS3 v%s | %s%s\n%s", rpcs3::version.to_string(), rpcs3::get_branch(), firmware_string, utils::get_system_info());
|
||||
|
||||
file_writer::log(logs::level::always, ver.text.data(), ver.text.size());
|
||||
file_writer::log(logs::level::always, "\n", 1);
|
||||
messages.emplace_back(std::move(ver));
|
||||
|
||||
// Write OS version
|
||||
stored_message os;
|
||||
os.m.ch = nullptr;
|
||||
os.m.sev = level::notice;
|
||||
os.stamp = 0;
|
||||
os.text = utils::get_OS_version();
|
||||
|
||||
file_writer::log(logs::level::notice, os.text.data(), os.text.size());
|
||||
file_writer::log(logs::level::notice, "\n", 1);
|
||||
messages.emplace_back(std::move(os));
|
||||
m_file.write(text, size);
|
||||
}
|
||||
|
||||
void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::string& prefix, const std::string& _text)
|
||||
{
|
||||
thread_local std::string text;
|
||||
std::string text; text.reserve(prefix.size() + _text.size() + 200);
|
||||
|
||||
// Used character: U+00B7 (Middle Dot)
|
||||
switch (msg.sev)
|
||||
@@ -630,24 +259,23 @@ void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::st
|
||||
case level::warning: text = u8"·W "; break;
|
||||
case level::notice: text = u8"·! "; break;
|
||||
case level::trace: text = u8"·T "; break;
|
||||
case level::_uninit: text = u8"· "; break;
|
||||
}
|
||||
|
||||
// Print µs timestamp
|
||||
// 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.empty())
|
||||
if (prefix.size() > 0)
|
||||
{
|
||||
text += "{";
|
||||
text += prefix;
|
||||
text += "} ";
|
||||
}
|
||||
|
||||
if (msg.ch && '\0' != *msg.ch->name)
|
||||
|
||||
if ('\0' != *msg.ch->name)
|
||||
{
|
||||
text += msg.ch->name;
|
||||
text += msg.sev == level::todo ? " TODO: " : ": ";
|
||||
@@ -656,9 +284,9 @@ void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::st
|
||||
{
|
||||
text += "TODO: ";
|
||||
}
|
||||
|
||||
|
||||
text += _text;
|
||||
text += '\n';
|
||||
|
||||
file_writer::log(msg.sev, text.data(), text.size());
|
||||
file_writer::log(text.data(), text.size());
|
||||
}
|
||||
|
||||
+17
-16
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
#include "StrFmt.h"
|
||||
#include <climits>
|
||||
|
||||
@@ -23,17 +23,14 @@ namespace logs
|
||||
|
||||
struct channel;
|
||||
|
||||
// Message information
|
||||
// Message information (temporary data)
|
||||
struct message
|
||||
{
|
||||
channel* ch;
|
||||
level sev;
|
||||
|
||||
private:
|
||||
// Send log message to global logger instance
|
||||
void broadcast(const char*, const fmt_type_info*, ...) const;
|
||||
|
||||
friend struct channel;
|
||||
void broadcast(const char*, const fmt_type_info*, const u64*);
|
||||
};
|
||||
|
||||
class listener
|
||||
@@ -70,16 +67,21 @@ namespace logs
|
||||
{
|
||||
}
|
||||
|
||||
// Formatting function
|
||||
template<typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE void format(level sev, const char* fmt, const Args&... args)
|
||||
{
|
||||
if (UNLIKELY(sev <= enabled))
|
||||
{
|
||||
message{this, sev}.broadcast(fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
}
|
||||
|
||||
#define GEN_LOG_METHOD(_sev)\
|
||||
const message msg_##_sev{this, level::_sev};\
|
||||
template <typename... Args>\
|
||||
void _sev(const char* fmt, const Args&... args)\
|
||||
template<typename... Args>\
|
||||
SAFE_BUFFERS void _sev(const char* fmt, const Args&... args)\
|
||||
{\
|
||||
if (UNLIKELY(level::_sev <= enabled))\
|
||||
{\
|
||||
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};\
|
||||
msg_##_sev.broadcast(fmt, type_list, u64{fmt_unveil<Args>::get(args)}...);\
|
||||
}\
|
||||
return format<Args...>(level::_sev, fmt, args...);\
|
||||
}
|
||||
|
||||
GEN_LOG_METHOD(fatal)
|
||||
@@ -102,6 +104,7 @@ namespace logs
|
||||
extern channel HLE;
|
||||
extern channel PPU;
|
||||
extern channel SPU;
|
||||
extern channel ARMv7;
|
||||
|
||||
// Log level control: set all channels to level::notice
|
||||
void reset();
|
||||
@@ -110,8 +113,6 @@ namespace logs
|
||||
void set_level(const std::string&, level);
|
||||
}
|
||||
|
||||
#define LOG_CHANNEL(ch, ...) ::logs::channel ch(#ch, ##__VA_ARGS__)
|
||||
|
||||
// Legacy:
|
||||
|
||||
#define LOG_SUCCESS(ch, fmt, ...) logs::ch.success("" fmt, ##__VA_ARGS__)
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
class MTProgressDialog : public wxDialog
|
||||
{
|
||||
wxGauge** m_gauge;
|
||||
wxStaticText** m_msg;
|
||||
|
||||
wxArrayLong m_maximum;
|
||||
const u8 m_cores;
|
||||
|
||||
static const uint layout = 16;
|
||||
static const uint maxdial = 65536;
|
||||
wxArrayInt m_lastupdate;
|
||||
|
||||
public:
|
||||
MTProgressDialog(wxWindow* parent, const wxSize& size, const wxString& title,
|
||||
const wxString& msg, const wxArrayLong& maximum, const u8 cores)
|
||||
: wxDialog(parent, wxID_ANY, title, wxDefaultPosition)
|
||||
, m_maximum(maximum)
|
||||
, m_cores(cores)
|
||||
{
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_gauge = new wxGauge*[m_cores];
|
||||
m_msg = new wxStaticText*[m_cores];
|
||||
|
||||
m_lastupdate.SetCount(cores);
|
||||
|
||||
for(uint i=0; i<m_cores; ++i)
|
||||
{
|
||||
m_lastupdate[i] = -1;
|
||||
|
||||
m_msg[i] = new wxStaticText(this, wxID_ANY, msg);
|
||||
sizer->Add(m_msg[i], 0, wxLEFT | wxTOP, layout);
|
||||
|
||||
m_gauge[i] = new wxGauge(this, wxID_ANY, maxdial,
|
||||
wxDefaultPosition, wxDefaultSize,
|
||||
wxGA_HORIZONTAL );
|
||||
|
||||
sizer->Add(m_gauge[i], 0, wxLEFT | wxRIGHT | wxTOP | wxEXPAND, layout);
|
||||
m_gauge[i]->SetValue(0);
|
||||
|
||||
sizer->AddSpacer(5);
|
||||
}
|
||||
|
||||
SetSizerAndFit(sizer);
|
||||
if(size != wxDefaultSize)
|
||||
{
|
||||
SetSize(size);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxSize ws;
|
||||
ws.x = 400;
|
||||
ws.y = GetSize().y + 8;
|
||||
SetSize(ws);
|
||||
}
|
||||
|
||||
m_maximum.SetCount(m_cores);
|
||||
|
||||
Show();
|
||||
}
|
||||
|
||||
void Update(const u8 thread_id, const u64 value, const wxString& msg)
|
||||
{
|
||||
if(thread_id > m_cores) return;
|
||||
|
||||
const int curupdate = (int)(((double)value/(double)m_maximum[thread_id])*1000);
|
||||
if(curupdate == m_lastupdate[thread_id]) return;
|
||||
m_lastupdate[thread_id] = curupdate;
|
||||
|
||||
m_msg[thread_id]->SetLabel(msg);
|
||||
|
||||
if(value >= (u32)m_maximum[thread_id]) return;
|
||||
m_gauge[thread_id]->SetValue(((double)value / (double)m_maximum[thread_id]) * maxdial);
|
||||
}
|
||||
|
||||
const u32 GetMaxValue(const uint thread_id) const
|
||||
{
|
||||
if(thread_id > m_cores) return 0;
|
||||
return m_maximum[thread_id];
|
||||
}
|
||||
|
||||
void SetMaxFor(const uint thread_id, const u64 val)
|
||||
{
|
||||
if(thread_id > m_cores) return;
|
||||
m_maximum[thread_id] = val;
|
||||
m_lastupdate[thread_id] = 0;
|
||||
}
|
||||
|
||||
virtual void Close(bool force = false)
|
||||
{
|
||||
m_lastupdate.Empty();
|
||||
m_maximum.Empty();
|
||||
|
||||
wxDialog::Close(force);
|
||||
}
|
||||
};
|
||||
+15
-71
@@ -4,7 +4,6 @@
|
||||
#include "cfmt.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
@@ -24,81 +23,34 @@ void fmt_class_string<std::pair<const fmt_type_info*, u64>>::format(std::string&
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<fmt::base57>::format(std::string& out, u64 arg)
|
||||
{
|
||||
const auto& _arg = get_object(arg);
|
||||
|
||||
if (_arg.data && _arg.size)
|
||||
{
|
||||
// Precomputed tail sizes if input data is not multiple of 8
|
||||
static constexpr u8 s_tail[8] = {0, 2, 3, 5, 6, 7, 9, 10};
|
||||
|
||||
// Get full output size
|
||||
const std::size_t out_size = _arg.size / 8 * 11 + s_tail[_arg.size % 8];
|
||||
|
||||
out.resize(out.size() + out_size);
|
||||
|
||||
const auto ptr = &out.front() + (out.size() - out_size);
|
||||
|
||||
// Each 8 bytes of input data produce 11 bytes of base57 output
|
||||
for (std::size_t i = 0, p = 0; i < _arg.size; i += 8, p += 11)
|
||||
{
|
||||
// Load up to 8 bytes
|
||||
be_t<u64> be_value;
|
||||
|
||||
if (_arg.size - i < sizeof(be_value))
|
||||
{
|
||||
std::memset(&be_value, 0, sizeof(be_value));
|
||||
std::memcpy(&be_value, _arg.data + i, _arg.size - i);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy(&be_value, _arg.data + i, sizeof(be_value));
|
||||
}
|
||||
|
||||
u64 value = be_value;
|
||||
|
||||
for (int j = 10; j >= 0; j--)
|
||||
{
|
||||
if (p + j < out_size)
|
||||
{
|
||||
ptr[p + j] = "0123456789ACEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"[value % 57];
|
||||
}
|
||||
|
||||
value /= 57;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_class_string<const void*>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%p", arg);
|
||||
if (arg)
|
||||
{
|
||||
fmt::append(out, "%p", reinterpret_cast<const void*>(static_cast<std::uintptr_t>(arg)));
|
||||
}
|
||||
else
|
||||
{
|
||||
out += "(NULL)";
|
||||
}
|
||||
}
|
||||
|
||||
void fmt_class_string<const char*>::format(std::string& out, u64 arg)
|
||||
{
|
||||
if (arg)
|
||||
{
|
||||
out += reinterpret_cast<const char*>(arg);
|
||||
out += reinterpret_cast<const char*>(static_cast<std::uintptr_t>(arg));
|
||||
}
|
||||
else
|
||||
{
|
||||
out += "(NULLSTR)";
|
||||
out += "(NULL)";
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<std::string>::format(std::string& out, u64 arg)
|
||||
{
|
||||
out += get_object(arg);
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<std::string_view>::format(std::string& out, u64 arg)
|
||||
{
|
||||
out += get_object(arg);
|
||||
out += get_object(arg).c_str(); // TODO?
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -177,13 +129,13 @@ void fmt_class_string<ullong>::format(std::string& out, u64 arg)
|
||||
template <>
|
||||
void fmt_class_string<float>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%gf", static_cast<float>(std::bit_cast<f64>(arg)));
|
||||
fmt::append(out, "%gf", static_cast<float>(reinterpret_cast<f64&>(arg)));
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<double>::format(std::string& out, u64 arg)
|
||||
{
|
||||
fmt::append(out, "%g", std::bit_cast<f64>(arg));
|
||||
fmt::append(out, "%g", reinterpret_cast<f64&>(arg));
|
||||
}
|
||||
|
||||
template <>
|
||||
@@ -305,8 +257,8 @@ struct fmt::cfmt_src
|
||||
|
||||
void skip(std::size_t extra)
|
||||
{
|
||||
sup += extra + 1;
|
||||
args += extra + 1;
|
||||
++sup += extra;
|
||||
++args += extra;
|
||||
}
|
||||
|
||||
std::size_t fmt_string(std::string& out, std::size_t extra) const
|
||||
@@ -423,14 +375,6 @@ std::string fmt::to_upper(const std::string& string)
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string fmt::to_lower(const std::string& string)
|
||||
{
|
||||
std::string result;
|
||||
result.resize(string.size());
|
||||
std::transform(string.begin(), string.end(), result.begin(), ::tolower);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fmt::match(const std::string& source, const std::string& mask)
|
||||
{
|
||||
std::size_t source_position = 0, mask_position = 0;
|
||||
|
||||
+20
-33
@@ -62,7 +62,7 @@ struct fmt_unveil<T, std::enable_if_t<std::is_floating_point<T>::value && sizeof
|
||||
// Convert FP to f64 and reinterpret as u64
|
||||
static inline u64 get(const f64& arg)
|
||||
{
|
||||
return std::bit_cast<u64>(arg);
|
||||
return *reinterpret_cast<const u64*>(reinterpret_cast<const u8*>(&arg));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -91,9 +91,9 @@ struct fmt_unveil<T, std::enable_if_t<std::is_enum<T>::value>>
|
||||
template <typename T>
|
||||
struct fmt_unveil<T*, void>
|
||||
{
|
||||
using type = std::add_const_t<T>*;
|
||||
using type = const T*;
|
||||
|
||||
static inline u64 get(type arg)
|
||||
static inline u64 get(const T* arg)
|
||||
{
|
||||
return reinterpret_cast<std::uintptr_t>(arg);
|
||||
}
|
||||
@@ -102,9 +102,9 @@ struct fmt_unveil<T*, void>
|
||||
template <typename T, std::size_t N>
|
||||
struct fmt_unveil<T[N], void>
|
||||
{
|
||||
using type = std::add_const_t<T>*;
|
||||
using type = const T*;
|
||||
|
||||
static inline u64 get(type arg)
|
||||
static inline u64 get(const T* arg)
|
||||
{
|
||||
return reinterpret_cast<std::uintptr_t>(arg);
|
||||
}
|
||||
@@ -140,9 +140,18 @@ struct fmt_class_string
|
||||
// Enum -> string function type
|
||||
using convert_t = const char*(*)(T value);
|
||||
|
||||
// Enum -> string function registered
|
||||
static convert_t convert_enum;
|
||||
|
||||
// Helper function (safely converts arg to enum value)
|
||||
static SAFE_BUFFERS FORCE_INLINE void format_enum(std::string& out, u64 arg, convert_t convert)
|
||||
{
|
||||
// Save convert function
|
||||
if (convert_enum == nullptr)
|
||||
{
|
||||
convert_enum = convert;
|
||||
}
|
||||
|
||||
const auto value = static_cast<std::underlying_type_t<T>>(arg);
|
||||
|
||||
// Check narrowing
|
||||
@@ -194,6 +203,9 @@ struct fmt_class_string
|
||||
static constexpr const char* unknown = nullptr;
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
const char*(*fmt_class_string<T, V>::convert_enum)(T) = nullptr;
|
||||
|
||||
template <>
|
||||
struct fmt_class_string<const void*, void>
|
||||
{
|
||||
@@ -238,38 +250,15 @@ using fmt_args_t = const u64(&&)[sizeof...(Args) + 1];
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
// Base-57 format helper
|
||||
struct base57
|
||||
{
|
||||
const uchar* data;
|
||||
std::size_t size;
|
||||
|
||||
template <typename T>
|
||||
base57(const T& arg)
|
||||
: data(reinterpret_cast<const uchar*>(&arg))
|
||||
, size(sizeof(T))
|
||||
{
|
||||
}
|
||||
|
||||
base57(const uchar* data, std::size_t size)
|
||||
: data(data)
|
||||
, size(size)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE const fmt_type_info* get_type_info()
|
||||
{
|
||||
// Constantly initialized null-terminated list of type-specific information
|
||||
static constexpr fmt_type_info result[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
static constexpr fmt_type_info result[sizeof...(Args) + 1]{fmt_type_info::make<Args>()...};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
constexpr const fmt_type_info type_info_v[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
|
||||
// Internal formatting function
|
||||
void raw_append(std::string& out, const char*, const fmt_type_info*, const u64*) noexcept;
|
||||
|
||||
@@ -277,8 +266,7 @@ namespace fmt
|
||||
template <typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE void append(std::string& out, const char* fmt, const Args&... args)
|
||||
{
|
||||
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
raw_append(out, fmt, type_list, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
raw_append(out, fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
|
||||
// Formatting function
|
||||
@@ -298,7 +286,6 @@ namespace fmt
|
||||
template <typename T = std::runtime_error, typename... Args>
|
||||
[[noreturn]] SAFE_BUFFERS FORCE_INLINE void throw_exception(const char* fmt, const Args&... args)
|
||||
{
|
||||
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
raw_throw_exception<T>(fmt, type_list, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
raw_throw_exception<T>(fmt, fmt::get_type_info<fmt_unveil_t<Args>...>(), fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
}
|
||||
|
||||
+2
-10
@@ -5,7 +5,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
// Copy null-terminated string from std::string to char array with truncation
|
||||
template <std::size_t N>
|
||||
@@ -25,12 +24,6 @@ inline void strcpy_trunc(char (&dst)[N], const char (&src)[N2])
|
||||
dst[count] = '\0';
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
inline bool ends_with(const std::string& src, const char (&end)[N])
|
||||
{
|
||||
return src.size() >= N - 1 && src.compare(src.size() - (N - 1), N - 1, end, N - 1) == 0;
|
||||
}
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
std::string replace_first(const std::string& src, const std::string& from, const std::string& to);
|
||||
@@ -101,10 +94,10 @@ namespace fmt
|
||||
auto end = source.end();
|
||||
for (--end; it != end; ++it)
|
||||
{
|
||||
result += std::string{*it} + separator;
|
||||
result += *it + separator;
|
||||
}
|
||||
|
||||
return result + std::string{source.back()};
|
||||
return result + source.back();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -135,7 +128,6 @@ namespace fmt
|
||||
}
|
||||
|
||||
std::string to_upper(const std::string& string);
|
||||
std::string to_lower(const std::string& string);
|
||||
|
||||
bool match(const std::string& source, const std::string& mask);
|
||||
}
|
||||
|
||||
+268
-601
File diff suppressed because it is too large
Load Diff
+205
-312
@@ -1,115 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include "mutex.h"
|
||||
#include "sema.h"
|
||||
#include "cond.h"
|
||||
#include "lockless.h"
|
||||
|
||||
// Report error and call std::abort(), defined in main.cpp
|
||||
[[noreturn]] void report_fatal_error(const std::string&);
|
||||
|
||||
// Will report exception and call std::abort() if put in catch(...)
|
||||
[[noreturn]] void catch_all_exceptions();
|
||||
|
||||
// Hardware core layout
|
||||
enum class native_core_arrangement : u32
|
||||
// Simple list of void() functors
|
||||
class task_stack
|
||||
{
|
||||
undefined,
|
||||
generic,
|
||||
intel_ht,
|
||||
amd_ccx
|
||||
};
|
||||
|
||||
enum class thread_class : u32
|
||||
{
|
||||
general,
|
||||
rsx,
|
||||
spu,
|
||||
ppu
|
||||
};
|
||||
|
||||
enum class thread_state
|
||||
{
|
||||
created, // Initial state
|
||||
detached, // The thread has been detached to destroy its own named_thread object (can be dangerously misused)
|
||||
aborting, // The thread has been joined in the destructor or explicitly aborted (mutually exclusive with detached)
|
||||
finished // Final state, always set at the end of thread execution
|
||||
};
|
||||
|
||||
template <class Context>
|
||||
class named_thread;
|
||||
|
||||
template <typename T>
|
||||
struct result_storage
|
||||
{
|
||||
alignas(T) std::byte data[sizeof(T)];
|
||||
|
||||
static constexpr bool empty = false;
|
||||
|
||||
using type = T;
|
||||
|
||||
T* get()
|
||||
struct task_base
|
||||
{
|
||||
std::unique_ptr<task_base> next;
|
||||
|
||||
virtual ~task_base();
|
||||
|
||||
virtual void invoke()
|
||||
{
|
||||
if (next)
|
||||
{
|
||||
next->invoke();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
struct task_type final : task_base
|
||||
{
|
||||
std::remove_reference_t<F> func;
|
||||
|
||||
task_type(F&& func)
|
||||
: func(std::forward<F>(func))
|
||||
{
|
||||
}
|
||||
|
||||
void invoke() final override
|
||||
{
|
||||
func();
|
||||
task_base::invoke();
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<task_base> m_stack;
|
||||
|
||||
public:
|
||||
task_stack() = default;
|
||||
|
||||
template <typename F>
|
||||
task_stack(F&& func)
|
||||
: m_stack(new task_type<F>(std::forward<F>(func)))
|
||||
{
|
||||
return reinterpret_cast<T*>(&data);
|
||||
}
|
||||
|
||||
const T* get() const
|
||||
void push(task_stack stack)
|
||||
{
|
||||
return reinterpret_cast<const T*>(&data);
|
||||
auto _top = stack.m_stack.release();
|
||||
auto _next = m_stack.release();
|
||||
m_stack.reset(_top);
|
||||
while (UNLIKELY(_top->next)) _top = _top->next.get();
|
||||
_top->next.reset(_next);
|
||||
}
|
||||
|
||||
void destroy() noexcept
|
||||
void reset()
|
||||
{
|
||||
get()->~T();
|
||||
m_stack.reset();
|
||||
}
|
||||
|
||||
void invoke() const
|
||||
{
|
||||
if (m_stack)
|
||||
{
|
||||
m_stack->invoke();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct result_storage<void>
|
||||
// Thread control class
|
||||
class thread_ctrl final
|
||||
{
|
||||
static constexpr bool empty = true;
|
||||
// Current thread
|
||||
static thread_local thread_ctrl* g_tls_this_thread;
|
||||
|
||||
using type = void;
|
||||
};
|
||||
|
||||
template <class Context, typename... Args>
|
||||
using result_storage_t = result_storage<std::invoke_result_t<Context, Args...>>;
|
||||
|
||||
// Detect on_abort() method (should return void)
|
||||
template <typename T, typename = void>
|
||||
struct thread_on_abort : std::bool_constant<false> {};
|
||||
|
||||
template <typename T>
|
||||
struct thread_on_abort<T, decltype(std::declval<named_thread<T>&>().on_abort())> : std::bool_constant<true> {};
|
||||
|
||||
// Detect on_cleanup() static member function (should return void) (in C++20 can use destroying delete instead)
|
||||
template <typename T, typename = void>
|
||||
struct thread_on_cleanup : std::bool_constant<false> {};
|
||||
|
||||
template <typename T>
|
||||
struct thread_on_cleanup<T, decltype(named_thread<T>::on_cleanup(std::declval<named_thread<T>*>()))> : std::bool_constant<true> {};
|
||||
|
||||
// Thread base class
|
||||
class thread_base
|
||||
{
|
||||
// Native thread entry point function type
|
||||
#ifdef _WIN32
|
||||
using native_entry = uint(__stdcall*)(void* arg);
|
||||
#else
|
||||
using native_entry = void*(*)(void* arg);
|
||||
#endif
|
||||
// Self pointer
|
||||
std::shared_ptr<thread_ctrl> m_self;
|
||||
|
||||
// Thread handle (platform-specific)
|
||||
atomic_t<std::uintptr_t> m_thread{0};
|
||||
|
||||
// Thread mutex
|
||||
mutable shared_mutex m_mutex;
|
||||
mutable semaphore<> m_mutex;
|
||||
|
||||
// Thread condition variable
|
||||
cond_variable m_cond;
|
||||
@@ -118,123 +104,77 @@ class thread_base
|
||||
atomic_t<u32> m_signal{0};
|
||||
|
||||
// Thread joining condition variable
|
||||
mutable cond_variable m_jcv;
|
||||
cond_variable m_jcv;
|
||||
|
||||
// Thread state
|
||||
atomic_t<thread_state> m_state = thread_state::created;
|
||||
// Remotely set or caught exception
|
||||
std::exception_ptr m_exception;
|
||||
|
||||
// Thread name
|
||||
lf_value<std::string> m_name;
|
||||
// Thread initial task or atexit task
|
||||
task_stack m_task;
|
||||
|
||||
//
|
||||
atomic_t<u64> m_cycles = 0;
|
||||
// Fixed name
|
||||
std::string m_name;
|
||||
|
||||
// Start thread
|
||||
void start(native_entry);
|
||||
static void start(const std::shared_ptr<thread_ctrl>&, task_stack);
|
||||
|
||||
// Called at the thread start
|
||||
void initialize();
|
||||
|
||||
// Called at the thread end, returns true if needs destruction
|
||||
bool finalize(int) noexcept;
|
||||
// Called at the thread end
|
||||
void finalize(std::exception_ptr) noexcept;
|
||||
|
||||
// Cleanup after possibly deleting the thread instance
|
||||
static void finalize() noexcept;
|
||||
|
||||
friend class thread_ctrl;
|
||||
|
||||
template <class Context>
|
||||
friend class named_thread;
|
||||
|
||||
protected:
|
||||
thread_base(std::string_view name);
|
||||
|
||||
~thread_base();
|
||||
|
||||
public:
|
||||
// Get CPU cycles since last time this function was called. First call returns 0.
|
||||
u64 get_cycles();
|
||||
|
||||
// Wait for the thread (it does NOT change thread state, and can be called from multiple threads)
|
||||
void join() const;
|
||||
|
||||
// Notify the thread
|
||||
void notify();
|
||||
};
|
||||
|
||||
// Collection of global function for current thread
|
||||
class thread_ctrl final
|
||||
{
|
||||
// Current thread
|
||||
static thread_local thread_base* g_tls_this_thread;
|
||||
|
||||
// Target cpu core layout
|
||||
static atomic_t<native_core_arrangement> g_native_core_layout;
|
||||
// Add task (atexit)
|
||||
static void _push(task_stack);
|
||||
|
||||
// Internal waiting function, may throw. Infinite value is -1.
|
||||
static bool _wait_for(u64 usec);
|
||||
|
||||
friend class thread_base;
|
||||
// Internal throwing function. Mutex must be locked and will be unlocked.
|
||||
[[noreturn]] void _throw();
|
||||
|
||||
// Internal notification function
|
||||
void _notify(cond_variable thread_ctrl::*);
|
||||
|
||||
public:
|
||||
// Get current thread name
|
||||
static std::string_view get_name()
|
||||
{
|
||||
return g_tls_this_thread->m_name.get();
|
||||
}
|
||||
thread_ctrl(std::string&& name);
|
||||
|
||||
thread_ctrl(const thread_ctrl&) = delete;
|
||||
|
||||
~thread_ctrl();
|
||||
|
||||
// Get thread name
|
||||
template <typename T>
|
||||
static std::string_view get_name(const named_thread<T>& thread)
|
||||
const std::string& get_name() const
|
||||
{
|
||||
return static_cast<const thread_base&>(thread).m_name.get();
|
||||
return m_name;
|
||||
}
|
||||
|
||||
// Set current thread name (not recommended)
|
||||
static void set_name(std::string_view name)
|
||||
{
|
||||
g_tls_this_thread->m_name.assign(name);
|
||||
}
|
||||
// Get exception
|
||||
std::exception_ptr get_exception() const;
|
||||
|
||||
// Set thread name (not recommended)
|
||||
template <typename T>
|
||||
static void set_name(named_thread<T>& thread, std::string_view name)
|
||||
{
|
||||
static_cast<thread_base&>(thread).m_name.assign(name);
|
||||
}
|
||||
// Set exception
|
||||
void set_exception(std::exception_ptr ptr);
|
||||
|
||||
template <typename T>
|
||||
static u64 get_cycles(named_thread<T>& thread)
|
||||
{
|
||||
return static_cast<thread_base&>(thread).get_cycles();
|
||||
}
|
||||
// Get thread result (may throw, simultaneous joining allowed)
|
||||
void join();
|
||||
|
||||
template <typename T>
|
||||
static void notify(named_thread<T>& thread)
|
||||
{
|
||||
static_cast<thread_base&>(thread).notify();
|
||||
}
|
||||
// Notify the thread
|
||||
void notify();
|
||||
|
||||
// Read current state
|
||||
static inline thread_state state()
|
||||
{
|
||||
return g_tls_this_thread->m_state;
|
||||
}
|
||||
|
||||
// Wait once with timeout. May spuriously return false.
|
||||
// Wait once with timeout. Abortable, may throw. May spuriously return false.
|
||||
static inline bool wait_for(u64 usec)
|
||||
{
|
||||
return _wait_for(usec);
|
||||
}
|
||||
|
||||
// Wait.
|
||||
// Wait. Abortable, may throw.
|
||||
static inline void wait()
|
||||
{
|
||||
_wait_for(-1);
|
||||
}
|
||||
|
||||
// Wait until pred().
|
||||
template <typename F, typename RT = std::invoke_result_t<F>>
|
||||
// Wait until pred(). Abortable, may throw.
|
||||
template<typename F, typename RT = std::result_of_t<F()>>
|
||||
static inline RT wait(F&& pred)
|
||||
{
|
||||
while (true)
|
||||
@@ -248,183 +188,136 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Wait eternally until aborted.
|
||||
[[noreturn]] static inline void eternalize()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_wait_for(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// Test exception (may throw).
|
||||
static void test();
|
||||
|
||||
// Get current thread (may be nullptr)
|
||||
static thread_base* get_current()
|
||||
static thread_ctrl* get_current()
|
||||
{
|
||||
return g_tls_this_thread;
|
||||
}
|
||||
|
||||
// Detect layout
|
||||
static void detect_cpu_layout();
|
||||
|
||||
// Returns a core affinity mask. Set whether to generate the high priority set or not
|
||||
static u64 get_affinity_mask(thread_class group);
|
||||
|
||||
// Sets the native thread priority
|
||||
static void set_native_priority(int priority);
|
||||
|
||||
// Sets the preferred affinity mask for this thread
|
||||
static void set_thread_affinity_mask(u64 mask);
|
||||
|
||||
// Spawn a detached named thread
|
||||
template <typename F>
|
||||
static void spawn(std::string_view name, F&& func)
|
||||
// Register function at thread exit (for the current thread)
|
||||
template<typename F>
|
||||
static inline void atexit(F&& func)
|
||||
{
|
||||
new named_thread<F>(thread_state::detached, name, std::forward<F>(func));
|
||||
_push(std::forward<F>(func));
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
thread_ctrl::start(out, std::forward<F>(func));
|
||||
}
|
||||
|
||||
// Named thread factory
|
||||
template<typename N, typename F>
|
||||
static inline void spawn(std::shared_ptr<thread_ctrl>& out, N&& name, F&& func)
|
||||
{
|
||||
out = std::make_shared<thread_ctrl>(std::forward<N>(name));
|
||||
|
||||
thread_ctrl::start(out, std::forward<F>(func));
|
||||
}
|
||||
|
||||
static void set_native_priority(int priority);
|
||||
static void set_ideal_processor_core(int core);
|
||||
};
|
||||
|
||||
// Derived from the callable object Context, possibly a lambda
|
||||
template <class Context>
|
||||
class named_thread final : public Context, result_storage_t<Context>, thread_base
|
||||
class named_thread
|
||||
{
|
||||
using result = result_storage_t<Context>;
|
||||
using thread = thread_base;
|
||||
|
||||
// Type-erased thread entry point
|
||||
#ifdef _WIN32
|
||||
static inline uint __stdcall entry_point(void* arg) try
|
||||
#else
|
||||
static inline void* entry_point(void* arg) try
|
||||
#endif
|
||||
{
|
||||
const auto _this = static_cast<named_thread*>(static_cast<thread*>(arg));
|
||||
|
||||
// Perform self-cleanup if necessary
|
||||
if (_this->entry_point())
|
||||
{
|
||||
// Call on_cleanup() static member function if it's available
|
||||
if constexpr (thread_on_cleanup<Context>())
|
||||
{
|
||||
Context::on_cleanup(_this);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete _this;
|
||||
}
|
||||
}
|
||||
|
||||
thread::finalize();
|
||||
return 0;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
catch_all_exceptions();
|
||||
}
|
||||
|
||||
bool entry_point()
|
||||
{
|
||||
thread::initialize();
|
||||
|
||||
if constexpr (result::empty)
|
||||
{
|
||||
// No result
|
||||
Context::operator()();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Construct the result using placement new (copy elision should happen)
|
||||
new (result::get()) typename result::type(Context::operator()());
|
||||
}
|
||||
|
||||
return thread::finalize(0);
|
||||
}
|
||||
|
||||
// Detached thread constructor
|
||||
named_thread(thread_state s, std::string_view name, Context&& f)
|
||||
: Context(std::forward<Context>(f))
|
||||
, thread(name)
|
||||
{
|
||||
thread::m_state.raw() = s;
|
||||
thread::start(&named_thread::entry_point);
|
||||
}
|
||||
|
||||
friend class thread_ctrl;
|
||||
// Pointer to managed resource (shared with actual thread)
|
||||
std::shared_ptr<thread_ctrl> m_thread;
|
||||
|
||||
public:
|
||||
// Normal forwarding constructor
|
||||
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<Context, Args&&...>>>
|
||||
named_thread(std::string_view name, Args&&... args)
|
||||
: Context(std::forward<Args>(args)...)
|
||||
, thread(name)
|
||||
{
|
||||
thread::start(&named_thread::entry_point);
|
||||
}
|
||||
named_thread();
|
||||
|
||||
// Lambda constructor, also the implicit deduction guide candidate
|
||||
named_thread(std::string_view name, Context&& f)
|
||||
: Context(std::forward<Context>(f))
|
||||
, thread(name)
|
||||
{
|
||||
thread::start(&named_thread::entry_point);
|
||||
}
|
||||
virtual ~named_thread();
|
||||
|
||||
// Deleted copy/move constructors + copy/move operators
|
||||
named_thread(const named_thread&) = delete;
|
||||
|
||||
named_thread& operator=(const named_thread&) = delete;
|
||||
// Get thread name
|
||||
virtual std::string get_name() const;
|
||||
|
||||
// Wait for the completion and access result (if not void)
|
||||
[[nodiscard]] decltype(auto) operator()()
|
||||
protected:
|
||||
// Start thread (cannot be called from the constructor: should throw in such case)
|
||||
void start_thread(const std::shared_ptr<void>& _this);
|
||||
|
||||
// Thread task (called in the thread)
|
||||
virtual void on_task() = 0;
|
||||
|
||||
// Thread finalization (called after on_task)
|
||||
virtual void on_exit() {}
|
||||
|
||||
// Called once upon thread spawn within the thread's own context
|
||||
virtual void on_spawn() {}
|
||||
|
||||
public:
|
||||
// ID initialization
|
||||
virtual void on_init(const std::shared_ptr<void>& _this)
|
||||
{
|
||||
thread::join();
|
||||
|
||||
if constexpr (!result::empty)
|
||||
{
|
||||
return *result::get();
|
||||
}
|
||||
return start_thread(_this);
|
||||
}
|
||||
|
||||
// Wait for the completion and access result (if not void)
|
||||
[[nodiscard]] decltype(auto) operator()() const
|
||||
// ID finalization
|
||||
virtual void on_stop()
|
||||
{
|
||||
thread::join();
|
||||
|
||||
if constexpr (!result::empty)
|
||||
{
|
||||
return *result::get();
|
||||
}
|
||||
m_thread->join();
|
||||
}
|
||||
|
||||
// Access thread state
|
||||
operator thread_state() const
|
||||
// Access thread_ctrl
|
||||
thread_ctrl* get() const
|
||||
{
|
||||
return thread::m_state.load();
|
||||
return m_thread.get();
|
||||
}
|
||||
|
||||
// Try to abort/detach
|
||||
named_thread& operator=(thread_state s)
|
||||
void join() const
|
||||
{
|
||||
if (s != thread_state::aborting && s != thread_state::detached)
|
||||
{
|
||||
ASSUME(0);
|
||||
}
|
||||
|
||||
if (thread::m_state.compare_and_swap_test(thread_state::created, s))
|
||||
{
|
||||
if (s == thread_state::aborting)
|
||||
{
|
||||
// Call on_abort() method if it's available
|
||||
if constexpr (thread_on_abort<Context>())
|
||||
{
|
||||
Context::on_abort();
|
||||
}
|
||||
}
|
||||
|
||||
thread::notify();
|
||||
}
|
||||
|
||||
return *this;
|
||||
return m_thread->join();
|
||||
}
|
||||
|
||||
// Context type doesn't need virtual destructor
|
||||
~named_thread()
|
||||
void notify() const
|
||||
{
|
||||
*this = thread_state::aborting;
|
||||
thread::join();
|
||||
|
||||
if constexpr (!result::empty)
|
||||
{
|
||||
result::destroy();
|
||||
}
|
||||
return m_thread->notify();
|
||||
}
|
||||
};
|
||||
|
||||
// Wrapper for named thread, joins automatically in the destructor, can only be used in function scope
|
||||
class scope_thread final
|
||||
{
|
||||
std::shared_ptr<thread_ctrl> m_thread;
|
||||
|
||||
public:
|
||||
template<typename N, typename F>
|
||||
scope_thread(N&& name, F&& func)
|
||||
{
|
||||
thread_ctrl::spawn(m_thread, std::forward<N>(name), std::forward<F>(func));
|
||||
}
|
||||
|
||||
// Deleted copy/move constructors + copy/move operators
|
||||
scope_thread(const scope_thread&) = delete;
|
||||
|
||||
// Destructor with exceptions allowed
|
||||
~scope_thread() noexcept(false)
|
||||
{
|
||||
m_thread->join();
|
||||
}
|
||||
|
||||
// Access thread_ctrl
|
||||
thread_ctrl* get() const
|
||||
{
|
||||
return m_thread.get();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
class Timer
|
||||
@@ -51,11 +49,4 @@ public:
|
||||
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(now - m_start).count();
|
||||
}
|
||||
|
||||
u64 GetMsSince(std::chrono::steady_clock::time_point timestamp)
|
||||
{
|
||||
std::chrono::steady_clock::time_point now = m_stopped ? m_end : std::chrono::steady_clock::now();
|
||||
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(now - timestamp).count();
|
||||
}
|
||||
};
|
||||
|
||||
+5
-195
@@ -7,27 +7,10 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#ifdef __NR_memfd_create
|
||||
#elif __x86_64__
|
||||
#define __NR_memfd_create 319
|
||||
#elif __aarch64__
|
||||
#define __NR_memfd_create 279
|
||||
#endif
|
||||
|
||||
static int memfd_create_(const char *name, uint flags)
|
||||
{
|
||||
return syscall(__NR_memfd_create, name, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace utils
|
||||
{
|
||||
// Convert memory protection (internal)
|
||||
@@ -80,199 +63,26 @@ namespace utils
|
||||
#ifdef _WIN32
|
||||
verify(HERE), ::VirtualAlloc(pointer, size, MEM_COMMIT, +prot);
|
||||
#else
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), size + ((u64)pointer & 4095), +prot) != -1;
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), +prot) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_decommit(void* pointer, std::size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
verify(HERE), ::VirtualFree(pointer, size, MEM_DECOMMIT);
|
||||
verify(HERE), ::VirtualFree(pointer, 0, MEM_DECOMMIT);
|
||||
#else
|
||||
verify(HERE), ::mmap(pointer, size, PROT_NONE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_reset(void* pointer, std::size_t size, protection prot)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
memory_decommit(pointer, size);
|
||||
memory_commit(pointer, size, prot);
|
||||
#else
|
||||
verify(HERE), ::mmap(pointer, size, +prot, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_release(void* pointer, std::size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
verify(HERE), ::VirtualFree(pointer, 0, MEM_RELEASE);
|
||||
#else
|
||||
verify(HERE), ::munmap(pointer, size) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_protect(void* pointer, std::size_t size, protection prot)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
for (u64 addr = (u64)pointer, end = addr + size; addr < end;)
|
||||
{
|
||||
const u64 boundary = (addr + 0x10000) & -0x10000;
|
||||
const u64 block_size = std::min(boundary, end) - addr;
|
||||
|
||||
DWORD old;
|
||||
if (!::VirtualProtect((LPVOID)addr, block_size, +prot, &old))
|
||||
{
|
||||
fmt::throw_exception("VirtualProtect failed (%p, 0x%x, addr=0x%x, error=%#x)", pointer, size, addr, GetLastError());
|
||||
}
|
||||
|
||||
// Next region
|
||||
addr += block_size;
|
||||
}
|
||||
DWORD old;
|
||||
verify(HERE), ::VirtualProtect(pointer, size, +prot, &old);
|
||||
#else
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), size + ((u64)pointer & 4095), +prot) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
shm::shm(u32 size)
|
||||
: m_size(::align(size, 0x10000))
|
||||
{
|
||||
#ifdef _WIN32
|
||||
m_handle = ::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, m_size, NULL);
|
||||
verify(HERE), m_handle != INVALID_HANDLE_VALUE;
|
||||
#elif __linux__
|
||||
m_file = ::memfd_create_("", 0);
|
||||
verify(HERE), m_file >= 0;
|
||||
verify(HERE), ::ftruncate(m_file, m_size) >= 0;
|
||||
#else
|
||||
while ((m_file = ::shm_open("/rpcs3-mem1", O_RDWR | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) == -1)
|
||||
{
|
||||
if (m_file == -1 && errno == EMFILE)
|
||||
{
|
||||
fmt::throw_exception("Too many open files. Raise the limit and try again.");
|
||||
}
|
||||
|
||||
verify(HERE), errno == EEXIST;
|
||||
}
|
||||
|
||||
verify(HERE), ::shm_unlink("/rpcs3-mem1") >= 0;
|
||||
verify(HERE), ::ftruncate(m_file, m_size) >= 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
shm::~shm()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
::CloseHandle(m_handle);
|
||||
#else
|
||||
::close(m_file);
|
||||
#endif
|
||||
}
|
||||
|
||||
u8* shm::map(void* ptr, protection prot) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD access = FILE_MAP_WRITE;
|
||||
switch (prot)
|
||||
{
|
||||
case protection::rw:
|
||||
case protection::ro:
|
||||
case protection::no:
|
||||
break;
|
||||
case protection::wx:
|
||||
case protection::rx:
|
||||
access |= FILE_MAP_EXECUTE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (auto ret = static_cast<u8*>(::MapViewOfFileEx(m_handle, access, 0, 0, m_size, ptr)))
|
||||
{
|
||||
if (prot != protection::rw && prot != protection::wx)
|
||||
{
|
||||
DWORD old;
|
||||
if (!::VirtualProtect(ret, m_size, +prot, &old))
|
||||
{
|
||||
::UnmapViewOfFile(ret);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
#else
|
||||
return static_cast<u8*>(::mmap((void*)((u64)ptr & -0x10000), m_size, +prot, MAP_SHARED | (ptr ? MAP_FIXED : 0), m_file, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
u8* shm::map_critical(void* ptr, protection prot)
|
||||
{
|
||||
const auto target = (u8*)((u64)ptr & -0x10000);
|
||||
|
||||
#ifdef _WIN32
|
||||
::MEMORY_BASIC_INFORMATION mem;
|
||||
if (!::VirtualQuery(target, &mem, sizeof(mem)) || mem.State != MEM_RESERVE || !::VirtualFree(mem.AllocationBase, 0, MEM_RELEASE))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto base = (u8*)mem.AllocationBase;
|
||||
const auto size = mem.RegionSize + (target - base);
|
||||
|
||||
if (base < target && !::VirtualAlloc(base, target - base, MEM_RESERVE, PAGE_NOACCESS))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (target + m_size < base + size && !::VirtualAlloc(target + m_size, base + size - target - m_size, MEM_RESERVE, PAGE_NOACCESS))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return this->map(target, prot);
|
||||
}
|
||||
|
||||
void shm::unmap(void* ptr) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
::UnmapViewOfFile(ptr);
|
||||
#else
|
||||
::munmap(ptr, m_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void shm::unmap_critical(void* ptr)
|
||||
{
|
||||
const auto target = (u8*)((u64)ptr & -0x10000);
|
||||
|
||||
this->unmap(target);
|
||||
|
||||
#ifdef _WIN32
|
||||
::MEMORY_BASIC_INFORMATION mem, mem2;
|
||||
if (!::VirtualQuery(target - 1, &mem, sizeof(mem)) || !::VirtualQuery(target + m_size, &mem2, sizeof(mem2)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mem.State == MEM_RESERVE && !::VirtualFree(mem.AllocationBase, 0, MEM_RELEASE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mem2.State == MEM_RESERVE && !::VirtualFree(mem2.AllocationBase, 0, MEM_RELEASE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto size1 = mem.State == MEM_RESERVE ? target - (u8*)mem.AllocationBase : 0;
|
||||
const auto size2 = mem2.State == MEM_RESERVE ? mem2.RegionSize : 0;
|
||||
|
||||
if (!::VirtualAlloc(mem.State == MEM_RESERVE ? mem.AllocationBase : target, m_size + size1 + size2, MEM_RESERVE, PAGE_NOACCESS))
|
||||
{
|
||||
return;
|
||||
}
|
||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), +prot) != -1;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
// Memory protection type
|
||||
@@ -27,52 +25,11 @@ namespace utils
|
||||
*/
|
||||
void memory_commit(void* pointer, std::size_t size, protection prot = protection::rw);
|
||||
|
||||
// Decommit all memory committed via commit_page_memory.
|
||||
/**
|
||||
* Decommit all memory committed via commit_page_memory.
|
||||
*/
|
||||
void memory_decommit(void* pointer, std::size_t size);
|
||||
|
||||
// Decommit all memory and commit it again.
|
||||
void memory_reset(void* pointer, std::size_t size, protection prot = protection::rw);
|
||||
|
||||
// Free memory after reserved by memory_reserve, should specify original size
|
||||
void memory_release(void* pointer, std::size_t size);
|
||||
|
||||
// Set memory protection
|
||||
void memory_protect(void* pointer, std::size_t size, protection prot);
|
||||
|
||||
// Shared memory handle
|
||||
class shm
|
||||
{
|
||||
#ifdef _WIN32
|
||||
void* m_handle;
|
||||
#else
|
||||
int m_file;
|
||||
#endif
|
||||
u32 m_size;
|
||||
|
||||
public:
|
||||
explicit shm(u32 size);
|
||||
|
||||
shm(const shm&) = delete;
|
||||
|
||||
shm& operator=(const shm&) = delete;
|
||||
|
||||
~shm();
|
||||
|
||||
// Map shared memory
|
||||
u8* map(void* ptr, protection prot = protection::rw) const;
|
||||
|
||||
// Map shared memory over reserved memory region, which is unsafe (non-atomic) under Win32
|
||||
u8* map_critical(void* ptr, protection prot = protection::rw);
|
||||
|
||||
// Unmap shared memory
|
||||
void unmap(void* ptr) const;
|
||||
|
||||
// Unmap shared memory, undoing map_critical
|
||||
void unmap_critical(void* ptr);
|
||||
|
||||
u32 size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,592 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "StrFmt.h"
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class address_range_vector;
|
||||
|
||||
|
||||
/**
|
||||
* Constexprs
|
||||
*/
|
||||
constexpr inline u32 page_start(u32 addr)
|
||||
{
|
||||
return addr & ~4095u;
|
||||
}
|
||||
|
||||
constexpr inline u32 next_page(u32 addr)
|
||||
{
|
||||
return page_start(addr + 4096u);
|
||||
}
|
||||
|
||||
constexpr inline u32 page_end(u32 addr)
|
||||
{
|
||||
return next_page(addr) - 1;
|
||||
}
|
||||
|
||||
constexpr inline u32 is_page_aligned(u32 addr)
|
||||
{
|
||||
return page_start(addr) == addr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Address Range utility class
|
||||
*/
|
||||
class address_range
|
||||
{
|
||||
public:
|
||||
u32 start = UINT32_MAX; // First address in range
|
||||
u32 end = 0; // Last address
|
||||
|
||||
private:
|
||||
// Helper constexprs
|
||||
static constexpr inline bool range_overlaps(u32 start1, u32 end1, u32 start2, u32 end2)
|
||||
{
|
||||
return (start1 <= end2 && start2 <= end1);
|
||||
}
|
||||
|
||||
static constexpr inline bool address_overlaps(u32 address, u32 start, u32 end)
|
||||
{
|
||||
return (start <= address && address <= end);
|
||||
}
|
||||
|
||||
static constexpr inline bool range_inside_range(u32 start1, u32 end1, u32 start2, u32 end2)
|
||||
{
|
||||
return (start1 >= start2 && end1 <= end2);
|
||||
}
|
||||
|
||||
address_range(u32 _start, u32 _end) : start(_start), end(_end) {}
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
address_range() = default;
|
||||
address_range(const address_range &other) : start(other.start), end(other.end) {}
|
||||
|
||||
static inline address_range start_length(u32 _start, u32 _length)
|
||||
{
|
||||
return address_range(_start, _start + (_length - 1));
|
||||
}
|
||||
|
||||
static inline address_range start_end(u32 _start, u32 _end)
|
||||
{
|
||||
return address_range(_start, _end);
|
||||
}
|
||||
|
||||
// Length
|
||||
u32 length() const
|
||||
{
|
||||
AUDIT(valid());
|
||||
return end - start + 1;
|
||||
}
|
||||
|
||||
void set_length(const u32 new_length)
|
||||
{
|
||||
end = start + new_length - 1;
|
||||
ASSERT(valid());
|
||||
}
|
||||
|
||||
u32 next_address() const
|
||||
{
|
||||
return end + 1;
|
||||
}
|
||||
|
||||
u32 prev_address() const
|
||||
{
|
||||
return start - 1;
|
||||
}
|
||||
|
||||
// Overlapping checks
|
||||
bool overlaps(const address_range &other) const
|
||||
{
|
||||
AUDIT(valid() && other.valid());
|
||||
return range_overlaps(start, end, other.start, other.end);
|
||||
}
|
||||
|
||||
bool overlaps(const u32 addr) const
|
||||
{
|
||||
AUDIT(valid());
|
||||
return address_overlaps(addr, start, end);
|
||||
}
|
||||
|
||||
bool inside(const address_range &other) const
|
||||
{
|
||||
AUDIT(valid() && other.valid());
|
||||
return range_inside_range(start, end, other.start, other.end);
|
||||
}
|
||||
|
||||
inline bool inside(const address_range_vector &vec) const;
|
||||
inline bool overlaps(const address_range_vector &vec) const;
|
||||
|
||||
bool touches(const address_range &other) const
|
||||
{
|
||||
AUDIT(valid() && other.valid());
|
||||
// returns true if there is overlap, or if sections are side-by-side
|
||||
return overlaps(other) || other.start == next_address() || other.end == prev_address();
|
||||
}
|
||||
|
||||
// Utilities
|
||||
s32 signed_distance(const address_range &other) const
|
||||
{
|
||||
if (touches(other))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// other after this
|
||||
if (other.start > end)
|
||||
{
|
||||
return (s32)(other.start - end - 1);
|
||||
}
|
||||
|
||||
// this after other
|
||||
AUDIT(start > other.end);
|
||||
return -((s32)(start - other.end - 1));
|
||||
}
|
||||
|
||||
u32 distance(const address_range &other) const
|
||||
{
|
||||
if (touches(other))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// other after this
|
||||
if (other.start > end)
|
||||
{
|
||||
return (other.start - end - 1);
|
||||
}
|
||||
|
||||
// this after other
|
||||
AUDIT(start > other.end);
|
||||
return (start - other.end - 1);
|
||||
}
|
||||
|
||||
address_range get_min_max(const address_range &other) const
|
||||
{
|
||||
return {
|
||||
std::min(valid() ? start : UINT32_MAX, other.valid() ? other.start : UINT32_MAX),
|
||||
std::max(valid() ? end : 0, other.valid() ? other.end : 0)
|
||||
};
|
||||
}
|
||||
|
||||
void set_min_max(const address_range &other)
|
||||
{
|
||||
const address_range _range = get_min_max(other);
|
||||
start = _range.start;
|
||||
end = _range.end;
|
||||
}
|
||||
|
||||
bool is_page_range() const
|
||||
{
|
||||
return (valid() && start % 4096u == 0 && length() % 4096u == 0);
|
||||
}
|
||||
|
||||
address_range to_page_range() const
|
||||
{
|
||||
AUDIT(valid());
|
||||
return { page_start(start), page_end(end) };
|
||||
}
|
||||
|
||||
void page_align()
|
||||
{
|
||||
AUDIT(valid());
|
||||
start = page_start(start);
|
||||
end = page_end(end);
|
||||
AUDIT(is_page_range());
|
||||
}
|
||||
|
||||
address_range get_intersect(const address_range &clamp) const
|
||||
{
|
||||
if (!valid() || !clamp.valid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return { std::max(start, clamp.start), std::min(end, clamp.end) };
|
||||
}
|
||||
|
||||
void intersect(const address_range &clamp)
|
||||
{
|
||||
if (!clamp.valid())
|
||||
{
|
||||
invalidate();
|
||||
}
|
||||
else
|
||||
{
|
||||
start = std::max(start, clamp.start);
|
||||
end = std::min(end, clamp.end);
|
||||
}
|
||||
}
|
||||
|
||||
// Validity
|
||||
bool valid() const
|
||||
{
|
||||
return (start <= end);
|
||||
}
|
||||
|
||||
void invalidate()
|
||||
{
|
||||
start = UINT32_MAX;
|
||||
end = 0;
|
||||
}
|
||||
|
||||
// Comparison Operators
|
||||
bool operator ==(const address_range &other) const
|
||||
{
|
||||
return (start == other.start && end == other.end);
|
||||
}
|
||||
|
||||
bool operator !=(const address_range &other) const
|
||||
{
|
||||
return (start != other.start || end != other.end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug
|
||||
*/
|
||||
std::string str() const
|
||||
{
|
||||
return fmt::format("{0x%x->0x%x}", start, end);
|
||||
}
|
||||
};
|
||||
|
||||
static inline address_range page_for(u32 addr)
|
||||
{
|
||||
return address_range::start_end(page_start(addr), page_end(addr));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Address Range Vector utility class
|
||||
*
|
||||
* Collection of address_range objects. Allows for merging and removing ranges from the set.
|
||||
*/
|
||||
class address_range_vector
|
||||
{
|
||||
public:
|
||||
using vector_type = typename std::vector<address_range>;
|
||||
using iterator = typename vector_type::iterator;
|
||||
using const_iterator = typename vector_type::const_iterator;
|
||||
using size_type = typename vector_type::size_type;
|
||||
|
||||
private:
|
||||
vector_type data;
|
||||
|
||||
public:
|
||||
// Wrapped functions
|
||||
inline void reserve(size_t nr) { data.reserve(nr); }
|
||||
inline void clear() { data.clear(); }
|
||||
inline size_type size() const { return data.size(); }
|
||||
inline bool empty() const { return data.empty(); }
|
||||
inline address_range& operator[](size_type n) { return data[n]; }
|
||||
inline const address_range& operator[](size_type n) const { return data[n]; }
|
||||
inline iterator begin() { return data.begin(); }
|
||||
inline const_iterator begin() const { return data.begin(); }
|
||||
inline iterator end() { return data.end(); }
|
||||
inline const_iterator end() const { return data.end(); }
|
||||
|
||||
// Search for ranges that touch new_range. If found, merge instead of adding new_range.
|
||||
// When adding a new range, re-use invalid ranges whenever possible
|
||||
void merge(const address_range &new_range)
|
||||
{
|
||||
// Note the case where we have
|
||||
// AAAA BBBB
|
||||
// CCCC
|
||||
// If we have data={A,B}, and new_range=C, we have to merge A with C, then B with A and invalidate B
|
||||
|
||||
if (!new_range.valid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
address_range *found = nullptr;
|
||||
address_range *invalid = nullptr;
|
||||
|
||||
for (auto &existing : data)
|
||||
{
|
||||
if (!existing.valid())
|
||||
{
|
||||
invalid = &existing;
|
||||
continue;
|
||||
}
|
||||
|
||||
// range1 overlaps, is immediately before, or is immediately after range2
|
||||
if (existing.touches(new_range))
|
||||
{
|
||||
if (found != nullptr)
|
||||
{
|
||||
// Already found a match, merge and invalidate "existing"
|
||||
found->set_min_max(existing);
|
||||
existing.invalidate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// First match, merge "new_range"
|
||||
existing.set_min_max(new_range);
|
||||
found = &existing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found != nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (invalid != nullptr)
|
||||
{
|
||||
*invalid = new_range;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.push_back(new_range);
|
||||
}
|
||||
|
||||
AUDIT(check_consistency());
|
||||
}
|
||||
|
||||
void merge(const address_range_vector &other)
|
||||
{
|
||||
for (const address_range &new_range : other)
|
||||
{
|
||||
merge(new_range);
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude a given range from data
|
||||
void exclude(const address_range &exclusion)
|
||||
{
|
||||
// Note the case where we have
|
||||
// AAAAAAA
|
||||
// EEE
|
||||
// where data={A} and exclusion=E.
|
||||
// In this case, we need to reduce A to the head (before E starts), and then create a new address_range B for the tail (after E ends), i.e.
|
||||
// AA BB
|
||||
// EEE
|
||||
|
||||
if (!exclusion.valid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
address_range *invalid = nullptr; // try to re-use an invalid range instead of calling push_back
|
||||
|
||||
// We use index access because we might have to push_back within the loop, which could invalidate the iterators
|
||||
size_type _size = data.size();
|
||||
for (size_type n = 0; n < _size; ++n)
|
||||
{
|
||||
address_range &existing = data[n];
|
||||
|
||||
if (!existing.valid())
|
||||
{
|
||||
// Null
|
||||
invalid = &existing;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!existing.overlaps(exclusion))
|
||||
{
|
||||
// No overlap, skip
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool head_excluded = exclusion.overlaps(existing.start); // This section has its start inside excluded range
|
||||
const bool tail_excluded = exclusion.overlaps(existing.end); // This section has its end inside excluded range
|
||||
|
||||
if (head_excluded && tail_excluded)
|
||||
{
|
||||
// Cannot be salvaged, fully excluded
|
||||
existing.invalidate();
|
||||
invalid = &existing;
|
||||
}
|
||||
else if (head_excluded)
|
||||
{
|
||||
// Head overlaps, truncate head
|
||||
existing.start = exclusion.next_address();
|
||||
}
|
||||
else if (tail_excluded)
|
||||
{
|
||||
// Tail overlaps, truncate tail
|
||||
existing.end = exclusion.prev_address();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Section sits in the middle (see comment above function header)
|
||||
AUDIT(exclusion.inside(existing));
|
||||
const auto tail_end = existing.end;
|
||||
|
||||
// Head
|
||||
existing.end = exclusion.prev_address();
|
||||
|
||||
// Tail
|
||||
if (invalid != nullptr)
|
||||
{
|
||||
invalid->start = exclusion.next_address();
|
||||
invalid->end = tail_end;
|
||||
invalid = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// IMPORTANT: adding to data invalidates "existing". This must be done last!
|
||||
data.push_back(address_range::start_end(exclusion.next_address(), tail_end));
|
||||
}
|
||||
}
|
||||
}
|
||||
AUDIT(check_consistency());
|
||||
AUDIT(!overlaps(exclusion));
|
||||
}
|
||||
|
||||
void exclude(const address_range_vector &other)
|
||||
{
|
||||
for (const address_range &exclusion : other)
|
||||
{
|
||||
exclude(exclusion);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks the consistency of this vector.
|
||||
// Will fail if ranges within the vector overlap our touch each-other
|
||||
bool check_consistency() const
|
||||
{
|
||||
size_t _size = data.size();
|
||||
|
||||
for (size_t i = 0; i < _size; ++i)
|
||||
{
|
||||
const auto &r1 = data[i];
|
||||
if (!r1.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t j = i + 1; j < _size; ++j)
|
||||
{
|
||||
const auto &r2 = data[j];
|
||||
if (!r2.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (r1.touches(r2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test for overlap with a given range
|
||||
bool overlaps(const address_range &range) const
|
||||
{
|
||||
for (const address_range ¤t : data)
|
||||
{
|
||||
if (!current.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current.overlaps(range))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test for overlap with a given address_range vector
|
||||
bool overlaps(const address_range_vector &other) const
|
||||
{
|
||||
for (const address_range &rng1 : data)
|
||||
{
|
||||
if (!rng1.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const address_range &rng2 : other.data)
|
||||
{
|
||||
if (!rng2.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rng1.overlaps(rng2))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test if a given range is fully contained inside this vector
|
||||
bool contains(const address_range &range) const
|
||||
{
|
||||
for (const address_range &cur : *this)
|
||||
{
|
||||
if (!cur.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (range.inside(cur))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test if all ranges in this vector are full contained inside a specific range
|
||||
bool inside(const address_range &range) const
|
||||
{
|
||||
for (const address_range &cur : *this)
|
||||
{
|
||||
if (!cur.valid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cur.inside(range))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// These declarations must be done after address_range_vector has been defined
|
||||
bool address_range::inside(const address_range_vector &vec) const
|
||||
{
|
||||
return vec.contains(*this);
|
||||
}
|
||||
|
||||
bool address_range::overlaps(const address_range_vector &vec) const
|
||||
{
|
||||
return vec.overlaps(*this);
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
|
||||
|
||||
namespace std {
|
||||
static_assert(sizeof(size_t) >= 2 * sizeof(u32), "size_t must be at least twice the size of u32");
|
||||
|
||||
template <>
|
||||
struct hash<utils::address_range>
|
||||
{
|
||||
std::size_t operator()(const utils::address_range& k) const
|
||||
{
|
||||
// we can guarantee a unique hash since our type is 64 bits and size_t as well
|
||||
return (size_t{ k.start } << 32) | size_t{ k.end };
|
||||
}
|
||||
};
|
||||
}
|
||||
-228
@@ -1,228 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
inline u32 cntlz32(u32 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanReverse(&res, arg) || nonzero ? res ^ 31 : 32;
|
||||
#elif __LZCNT__
|
||||
return _lzcnt_u32(arg);
|
||||
#else
|
||||
return arg || nonzero ? __builtin_clz(arg) : 32;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 cntlz64(u64 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanReverse64(&res, arg) || nonzero ? res ^ 63 : 64;
|
||||
#elif __LZCNT__
|
||||
return _lzcnt_u64(arg);
|
||||
#else
|
||||
return arg || nonzero ? __builtin_clzll(arg) : 64;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u32 cnttz32(u32 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanForward(&res, arg) || nonzero ? res : 32;
|
||||
#elif __BMI__
|
||||
return _tzcnt_u32(arg);
|
||||
#else
|
||||
return arg || nonzero ? __builtin_ctz(arg) : 32;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 cnttz64(u64 arg, bool nonzero = false)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
ulong res;
|
||||
return _BitScanForward64(&res, arg) || nonzero ? res : 64;
|
||||
#elif __BMI__
|
||||
return _tzcnt_u64(arg);
|
||||
#else
|
||||
return arg || nonzero ? __builtin_ctzll(arg) : 64;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u8 popcnt32(u32 arg)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
const u32 a1 = arg & 0x55555555;
|
||||
const u32 a2 = (arg >> 1) & 0x55555555;
|
||||
const u32 a3 = a1 + a2;
|
||||
const u32 b1 = a3 & 0x33333333;
|
||||
const u32 b2 = (a3 >> 2) & 0x33333333;
|
||||
const u32 b3 = b1 + b2;
|
||||
const u32 c3 = (b3 + (b3 >> 4)) & 0x0f0f0f0f;
|
||||
const u32 d3 = c3 + (c3 >> 8);
|
||||
return static_cast<u8>(d3 + (d3 >> 16));
|
||||
#else
|
||||
return __builtin_popcount(arg);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Rotate helpers
|
||||
#if defined(__GNUG__)
|
||||
|
||||
inline u8 rol8(u8 x, u8 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft8)
|
||||
return __builtin_rotateleft8(x, n);
|
||||
#else
|
||||
u8 result = x;
|
||||
__asm__("rolb %[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u8 ror8(u8 x, u8 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright8)
|
||||
return __builtin_rotateright8(x, n);
|
||||
#else
|
||||
u8 result = x;
|
||||
__asm__("rorb %[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u16 rol16(u16 x, u16 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft16)
|
||||
return __builtin_rotateleft16(x, n);
|
||||
#else
|
||||
u16 result = x;
|
||||
__asm__("rolw %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u16 ror16(u16 x, u16 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright16)
|
||||
return __builtin_rotateright16(x, n);
|
||||
#else
|
||||
u16 result = x;
|
||||
__asm__("rorw %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u32 rol32(u32 x, u32 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft32)
|
||||
return __builtin_rotateleft32(x, n);
|
||||
#else
|
||||
u32 result = x;
|
||||
__asm__("roll %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u32 ror32(u32 x, u32 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright32)
|
||||
return __builtin_rotateright32(x, n);
|
||||
#else
|
||||
u32 result = x;
|
||||
__asm__("rorl %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 rol64(u64 x, u64 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft64)
|
||||
return __builtin_rotateleft64(x, n);
|
||||
#else
|
||||
u64 result = x;
|
||||
__asm__("rolq %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 ror64(u64 x, u64 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright64)
|
||||
return __builtin_rotateright64(x, n);
|
||||
#else
|
||||
u64 result = x;
|
||||
__asm__("rorq %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr u64 umulh64(u64 a, u64 b)
|
||||
{
|
||||
const __uint128_t x = a;
|
||||
const __uint128_t y = b;
|
||||
return (x * y) >> 64;
|
||||
}
|
||||
|
||||
constexpr s64 mulh64(s64 a, s64 b)
|
||||
{
|
||||
const __int128_t x = a;
|
||||
const __int128_t y = b;
|
||||
return (x * y) >> 64;
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
inline u8 rol8(u8 x, u8 n)
|
||||
{
|
||||
return _rotl8(x, n);
|
||||
}
|
||||
|
||||
inline u8 ror8(u8 x, u8 n)
|
||||
{
|
||||
return _rotr8(x, n);
|
||||
}
|
||||
|
||||
inline u16 rol16(u16 x, u16 n)
|
||||
{
|
||||
return _rotl16(x, (u8)n);
|
||||
}
|
||||
|
||||
inline u16 ror16(u16 x, u16 n)
|
||||
{
|
||||
return _rotr16(x, (u8)n);
|
||||
}
|
||||
|
||||
inline u32 rol32(u32 x, u32 n)
|
||||
{
|
||||
return _rotl(x, (int)n);
|
||||
}
|
||||
|
||||
inline u32 ror32(u32 x, u32 n)
|
||||
{
|
||||
return _rotr(x, (int)n);
|
||||
}
|
||||
|
||||
inline u64 rol64(u64 x, u64 n)
|
||||
{
|
||||
return _rotl64(x, (int)n);
|
||||
}
|
||||
|
||||
inline u64 ror64(u64 x, u64 n)
|
||||
{
|
||||
return _rotr64(x, (int)n);
|
||||
}
|
||||
|
||||
inline u64 umulh64(u64 x, u64 y)
|
||||
{
|
||||
return __umulh(x, y);
|
||||
}
|
||||
|
||||
inline s64 mulh64(s64 x, s64 y)
|
||||
{
|
||||
return __mulh(x, y);
|
||||
}
|
||||
#endif
|
||||
} // namespace utils
|
||||
+8
-78
@@ -1,5 +1,5 @@
|
||||
#include "bin_patch.h"
|
||||
#include <yaml-cpp/yaml.h>
|
||||
#include "yaml-cpp/yaml.h"
|
||||
#include "File.h"
|
||||
#include "Config.h"
|
||||
|
||||
@@ -10,18 +10,13 @@ void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case patch_type::load: return "load";
|
||||
case patch_type::byte: return "byte";
|
||||
case patch_type::le16: return "le16";
|
||||
case patch_type::le32: return "le32";
|
||||
case patch_type::le64: return "le64";
|
||||
case patch_type::bef32: return "bef32";
|
||||
case patch_type::bef64: return "bef64";
|
||||
case patch_type::be16: return "be16";
|
||||
case patch_type::be32: return "be32";
|
||||
case patch_type::be64: return "be64";
|
||||
case patch_type::lef32: return "lef32";
|
||||
case patch_type::lef64: return "lef64";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
@@ -32,89 +27,35 @@ void patch_engine::append(const std::string& patch)
|
||||
{
|
||||
if (fs::file f{patch})
|
||||
{
|
||||
YAML::Node root;
|
||||
|
||||
try
|
||||
{
|
||||
root = YAML::Load(f.to_string());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_FATAL(GENERAL, "Failed to load patch file %s\n%s thrown: %s", patch, typeid(e).name(), e.what());
|
||||
return;
|
||||
}
|
||||
auto root = YAML::Load(f.to_string());
|
||||
|
||||
for (auto pair : root)
|
||||
{
|
||||
auto& name = pair.first.Scalar();
|
||||
auto& data = m_map[name];
|
||||
|
||||
|
||||
for (auto patch : pair.second)
|
||||
{
|
||||
u64 type64 = 0;
|
||||
cfg::try_to_enum_value(&type64, &fmt_class_string<patch_type>::format, patch[0].Scalar());
|
||||
|
||||
struct patch info{};
|
||||
struct patch info;
|
||||
info.type = static_cast<patch_type>(type64);
|
||||
info.offset = patch[1].as<u32>(0);
|
||||
|
||||
switch (info.type)
|
||||
{
|
||||
case patch_type::load:
|
||||
{
|
||||
// Special syntax: copy named sequence (must be loaded before)
|
||||
const auto found = m_map.find(patch[1].Scalar());
|
||||
|
||||
if (found != m_map.end())
|
||||
{
|
||||
// Address modifier (optional)
|
||||
const u32 mod = patch[2].as<u32>(0);
|
||||
|
||||
for (const auto& rd : found->second)
|
||||
{
|
||||
info = rd;
|
||||
info.offset += mod;
|
||||
data.emplace_back(info);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: error
|
||||
break;
|
||||
}
|
||||
case patch_type::bef32:
|
||||
case patch_type::lef32:
|
||||
{
|
||||
info.value = std::bit_cast<u32>(patch[2].as<f32>());
|
||||
break;
|
||||
}
|
||||
case patch_type::bef64:
|
||||
case patch_type::lef64:
|
||||
{
|
||||
info.value = std::bit_cast<u64>(patch[2].as<f64>());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
info.value = patch[2].as<u64>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
info.offset = patch[1].as<u32>();
|
||||
info.value = patch[2].as<u64>();
|
||||
data.emplace_back(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t patch_engine::apply(const std::string& name, u8* dst) const
|
||||
void patch_engine::apply(const std::string& name, u8* dst) const
|
||||
{
|
||||
const auto found = m_map.find(name);
|
||||
|
||||
if (found == m_map.cend())
|
||||
{
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply modifications sequentially
|
||||
@@ -124,11 +65,6 @@ std::size_t patch_engine::apply(const std::string& name, u8* dst) const
|
||||
|
||||
switch (p.type)
|
||||
{
|
||||
case patch_type::load:
|
||||
{
|
||||
// Invalid in this context
|
||||
break;
|
||||
}
|
||||
case patch_type::byte:
|
||||
{
|
||||
*ptr = static_cast<u8>(p.value);
|
||||
@@ -140,13 +76,11 @@ std::size_t patch_engine::apply(const std::string& name, u8* dst) const
|
||||
break;
|
||||
}
|
||||
case patch_type::le32:
|
||||
case patch_type::lef32:
|
||||
{
|
||||
*reinterpret_cast<le_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
|
||||
break;
|
||||
}
|
||||
case patch_type::le64:
|
||||
case patch_type::lef64:
|
||||
{
|
||||
*reinterpret_cast<le_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
|
||||
break;
|
||||
@@ -157,19 +91,15 @@ std::size_t patch_engine::apply(const std::string& name, u8* dst) const
|
||||
break;
|
||||
}
|
||||
case patch_type::be32:
|
||||
case patch_type::bef32:
|
||||
{
|
||||
*reinterpret_cast<be_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
|
||||
break;
|
||||
}
|
||||
case patch_type::be64:
|
||||
case patch_type::bef64:
|
||||
{
|
||||
*reinterpret_cast<be_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found->second.size();
|
||||
}
|
||||
|
||||
@@ -7,18 +7,13 @@
|
||||
|
||||
enum class patch_type
|
||||
{
|
||||
load,
|
||||
byte,
|
||||
le16,
|
||||
le32,
|
||||
le64,
|
||||
lef32,
|
||||
lef64,
|
||||
be16,
|
||||
be32,
|
||||
be64,
|
||||
bef32,
|
||||
bef64,
|
||||
};
|
||||
|
||||
class patch_engine
|
||||
@@ -37,6 +32,6 @@ public:
|
||||
// Load from file
|
||||
void append(const std::string& path);
|
||||
|
||||
// Apply patch (returns the number of entries applied)
|
||||
std::size_t apply(const std::string& name, u8* dst) const;
|
||||
// Apply patch
|
||||
void apply(const std::string& name, u8* dst) const;
|
||||
};
|
||||
|
||||
+603
-284
File diff suppressed because it is too large
Load Diff
+19
-25
@@ -1,14 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "asm.h"
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
static const size_t size_dropped = std::numeric_limits<size_t>::max();
|
||||
|
||||
/*
|
||||
C-style format parser. Appends formatted string to `out`, returns number of characters written.
|
||||
`out`: mutable reference to std::string, std::vector<char> or other compatible container
|
||||
@@ -43,7 +40,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
const auto drop_sequence = [&]
|
||||
{
|
||||
out.insert(out.end(), fmt - ctx.size, fmt);
|
||||
ctx.size = size_dropped;
|
||||
ctx.size = -1;
|
||||
};
|
||||
|
||||
const auto read_decimal = [&](uint result) -> uint
|
||||
@@ -59,7 +56,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
|
||||
const auto write_octal = [&](u64 value, u64 min_num)
|
||||
{
|
||||
out.resize(out.size() + std::max<u64>(min_num, 66 / 3 - (utils::cntlz64(value | 1, true) + 2) / 3), '0');
|
||||
out.resize(out.size() + std::max<u64>(min_num, 66 / 3 - (cntlz64(value | 1, true) + 2) / 3), '0');
|
||||
|
||||
// Write in reversed order
|
||||
for (auto i = out.rbegin(); value; i++, value /= 8)
|
||||
@@ -70,8 +67,8 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
|
||||
const auto write_hex = [&](u64 value, bool upper, u64 min_num)
|
||||
{
|
||||
out.resize(out.size() + std::max<u64>(min_num, 64 / 4 - utils::cntlz64(value | 1, true) / 4), '0');
|
||||
|
||||
out.resize(out.size() + std::max<u64>(min_num, 64 / 4 - cntlz64(value | 1, true) / 4), '0');
|
||||
|
||||
// Write in reversed order
|
||||
for (auto i = out.rbegin(); value; i++, value /= 16)
|
||||
{
|
||||
@@ -114,7 +111,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
ctx = {0};
|
||||
out.push_back(ch);
|
||||
}
|
||||
else if (ctx.size == size_dropped)
|
||||
else if (ctx.size == -1)
|
||||
{
|
||||
out.push_back(ch);
|
||||
}
|
||||
@@ -144,7 +141,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
{
|
||||
ctx.width = read_decimal(ch - '0');
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -160,7 +157,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
ctx.width = std::abs(warg);
|
||||
ctx.left |= warg < 0;
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -310,7 +307,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
|
||||
const std::size_t start = out.size();
|
||||
const std::size_t size1 = src.fmt_string(out, ctx.args);
|
||||
|
||||
|
||||
if (ctx.dot && size1 > ctx.prec)
|
||||
{
|
||||
// Shrink if necessary
|
||||
@@ -412,10 +409,9 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
}
|
||||
|
||||
const u64 mask =
|
||||
ctx.type == 1 ? 0xff :
|
||||
ctx.type == 2 ? 0xffff :
|
||||
ctx.type == 4 ? 0xffff'ffffu :
|
||||
0xffff'ffff'ffff'ffffu;
|
||||
ctx.type == 1 ? 0xffull :
|
||||
ctx.type == 2 ? 0xffffull :
|
||||
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
|
||||
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
@@ -469,10 +465,9 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
}
|
||||
|
||||
const u64 mask =
|
||||
ctx.type == 1 ? 0xff :
|
||||
ctx.type == 2 ? 0xffff :
|
||||
ctx.type == 4 ? 0xffff'ffffu :
|
||||
0xffff'ffff'ffff'ffffu;
|
||||
ctx.type == 1 ? 0xffull :
|
||||
ctx.type == 2 ? 0xffffull :
|
||||
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
|
||||
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
@@ -533,10 +528,9 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
}
|
||||
|
||||
const u64 mask =
|
||||
ctx.type == 1 ? 0xff :
|
||||
ctx.type == 2 ? 0xffff :
|
||||
ctx.type == 4 ? 0xffff'ffffu :
|
||||
0xffff'ffff'ffff'ffffu;
|
||||
ctx.type == 1 ? 0xffull :
|
||||
ctx.type == 2 ? 0xffffull :
|
||||
ctx.type == 4 ? 0xffffffffull : 0xffffffffffffffffull;
|
||||
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
@@ -582,7 +576,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
// Add padding if necessary
|
||||
out.insert(ctx.left ? out.end() : out.begin() + start, ctx.width - size2, ' ');
|
||||
}
|
||||
|
||||
|
||||
src.skip(ctx.args);
|
||||
ctx = {0};
|
||||
break;
|
||||
@@ -631,7 +625,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
}
|
||||
|
||||
// Handle unfinished sequence
|
||||
if (ctx.size && ctx.size != size_dropped)
|
||||
if (ctx.size && ctx.size != -1)
|
||||
{
|
||||
fmt--, drop_sequence();
|
||||
}
|
||||
|
||||
+77
-447
@@ -1,6 +1,5 @@
|
||||
#include "cond.h"
|
||||
#include "sync.h"
|
||||
#include "lockless.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
@@ -10,468 +9,99 @@
|
||||
|
||||
bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept
|
||||
{
|
||||
verify("cond_variable overflow" HERE), (_old & 0xffff) != 0xffff; // Very unlikely: it requires 65535 distinct threads to wait simultaneously
|
||||
|
||||
return balanced_wait_until(m_value, _timeout, [&](u32& value, auto... ret) -> int
|
||||
{
|
||||
if (value >> 16)
|
||||
{
|
||||
// Success
|
||||
value -= 0x10001;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
// Retire
|
||||
value -= 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
verify(HERE), _old != -1; // Very unlikely: it requires 2^32 distinct threads to wait simultaneously
|
||||
|
||||
#ifdef _WIN32
|
||||
if (_old >= 0x10000 && !OptWaitOnAddress && m_value)
|
||||
LARGE_INTEGER timeout;
|
||||
timeout.QuadPart = _timeout * -10;
|
||||
|
||||
if (HRESULT rc = NtWaitForKeyedEvent(nullptr, &m_value, false, _timeout == -1 ? nullptr : &timeout))
|
||||
{
|
||||
// Workaround possibly stolen signal
|
||||
imp_wake(1);
|
||||
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
|
||||
{
|
||||
// TODO (notify_one)
|
||||
balanced_awaken<true>(m_value, m_value.atomic_op([&](u32& value) -> u32
|
||||
#ifdef _WIN32
|
||||
// Try to subtract required amount of waiters
|
||||
const u32 count = m_value.atomic_op([=](u32& value)
|
||||
{
|
||||
// Subtract already signaled number from total amount of waiters
|
||||
const u32 can_sig = (value & 0xffff) - (value >> 16);
|
||||
const u32 num_sig = std::min<u32>(can_sig, _count);
|
||||
|
||||
value += num_sig << 16;
|
||||
return num_sig;
|
||||
}));
|
||||
}
|
||||
|
||||
bool notifier::imp_try_lock(u32 count)
|
||||
{
|
||||
return m_counter.atomic_op([&](u32& value)
|
||||
{
|
||||
if ((value % (max_readers + 1)) + count <= max_readers)
|
||||
if (value > _count)
|
||||
{
|
||||
value += count;
|
||||
return true;
|
||||
value -= _count;
|
||||
return _count;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void notifier::imp_unlock(u32 count)
|
||||
{
|
||||
const u32 counter = m_counter.sub_fetch(count);
|
||||
|
||||
if (UNLIKELY(counter % (max_readers + 1)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (counter)
|
||||
{
|
||||
const u32 _old = m_counter.atomic_op([](u32& value) -> u32
|
||||
{
|
||||
if (value % (max_readers + 1))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return std::exchange(value, 0) / (max_readers + 1);
|
||||
});
|
||||
|
||||
const u32 wc = m_cond.m_value;
|
||||
|
||||
if (_old && wc)
|
||||
{
|
||||
m_cond.imp_wake(_old > wc ? wc : _old);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 notifier::imp_notify(u32 count)
|
||||
{
|
||||
return m_counter.atomic_op([&](u32& value) -> u32
|
||||
{
|
||||
if (const u32 add = value % (max_readers + 1))
|
||||
{
|
||||
// Mutex is locked
|
||||
const u32 result = add > count ? count : add;
|
||||
value += result * (max_readers + 1);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mutex is unlocked
|
||||
value = 0;
|
||||
return count;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool notifier::wait(u64 usec_timeout)
|
||||
{
|
||||
const u32 _old = m_cond.m_value.fetch_add(1);
|
||||
|
||||
if (max_readers < m_counter.fetch_op([](u32& value)
|
||||
{
|
||||
if (value > max_readers)
|
||||
{
|
||||
value -= max_readers;
|
||||
}
|
||||
|
||||
value -= 1;
|
||||
}))
|
||||
{
|
||||
// Return without waiting
|
||||
m_cond.imp_wait(_old, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool res = m_cond.imp_wait(_old, usec_timeout);
|
||||
|
||||
while (!try_lock_shared())
|
||||
{
|
||||
// TODO
|
||||
busy_wait();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool unique_cond::imp_wait(u64 _timeout) noexcept
|
||||
{
|
||||
// State transition: c_sig -> c_lock \ c_lock -> c_wait
|
||||
const u32 _old = m_value.fetch_sub(1);
|
||||
if (LIKELY(_old == c_sig))
|
||||
return true;
|
||||
|
||||
return balanced_wait_until(m_value, _timeout, [&](u32& value, auto... ret) -> int
|
||||
{
|
||||
if (value == c_sig)
|
||||
{
|
||||
value = c_lock;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
value = c_lock;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
void unique_cond::imp_notify() noexcept
|
||||
{
|
||||
auto [old, ok] = m_value.fetch_op([](u32& v)
|
||||
{
|
||||
if (UNLIKELY(v > 0 && v < c_sig))
|
||||
{
|
||||
v = c_sig;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return std::exchange(value, 0);
|
||||
});
|
||||
|
||||
verify(HERE), old <= c_sig;
|
||||
|
||||
if (LIKELY(!ok || old == c_lock))
|
||||
for (u32 i = count; i > 0; i--)
|
||||
{
|
||||
return;
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
#else
|
||||
for (u32 i = _count; i > 0; std::this_thread::yield())
|
||||
{
|
||||
const u32 value = m_value;
|
||||
|
||||
balanced_awaken(m_value, 1);
|
||||
}
|
||||
|
||||
bool shared_cond::imp_wait(u32 slot, u64 _timeout) noexcept
|
||||
{
|
||||
if (slot >= 32)
|
||||
{
|
||||
// Invalid argument, assume notified
|
||||
return true;
|
||||
}
|
||||
|
||||
const u64 wait_bit = c_wait << slot;
|
||||
const u64 lock_bit = c_lock << slot;
|
||||
|
||||
// Change state from c_lock to c_wait
|
||||
const u64 old_ = m_cvx32.fetch_op([=](u64& cvx32)
|
||||
{
|
||||
if (cvx32 & wait_bit)
|
||||
{
|
||||
// c_lock -> c_wait
|
||||
cvx32 &= ~(lock_bit & ~wait_bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
// c_sig -> c_lock
|
||||
cvx32 |= lock_bit;
|
||||
}
|
||||
});
|
||||
|
||||
if ((old_ & wait_bit) == 0)
|
||||
{
|
||||
// Already signaled, return without waiting
|
||||
return true;
|
||||
}
|
||||
|
||||
return balanced_wait_until(m_cvx32, _timeout, [&](u64& cvx32, auto... ret) -> int
|
||||
{
|
||||
if ((cvx32 & wait_bit) == 0)
|
||||
{
|
||||
// c_sig -> c_lock
|
||||
cvx32 |= lock_bit;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
// Retire
|
||||
cvx32 |= lock_bit;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
void shared_cond::imp_notify() noexcept
|
||||
{
|
||||
auto [old, ok] = m_cvx32.fetch_op([](u64& cvx32)
|
||||
{
|
||||
if (const u64 sig_mask = cvx32 & 0xffffffff)
|
||||
{
|
||||
cvx32 &= 0xffffffffull << 32;
|
||||
cvx32 |= sig_mask << 32;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Determine if some waiters need a syscall notification
|
||||
const u64 wait_mask = old & (~old >> 32);
|
||||
|
||||
if (UNLIKELY(!ok || !wait_mask))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
balanced_awaken<true>(m_cvx32, utils::popcnt32(wait_mask));
|
||||
}
|
||||
|
||||
void shared_cond::wait_all() noexcept
|
||||
{
|
||||
// Try to acquire waiting state without locking but only if there are other locks
|
||||
const auto [old_, result] = m_cvx32.fetch_op([](u64& cvx32) -> u64
|
||||
{
|
||||
// Check waiting alone
|
||||
if ((cvx32 & 0xffffffff) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Combine used bits and invert to find least significant bit unused
|
||||
const u32 slot = utils::cnttz64(~((cvx32 & 0xffffffff) | (cvx32 >> 32)), true);
|
||||
|
||||
// Set waiting bit (does nothing if all slots are used)
|
||||
cvx32 |= (1ull << slot) & 0xffffffff;
|
||||
return 1ull << slot;
|
||||
});
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (result > 0xffffffffu)
|
||||
{
|
||||
// All slots are used, fallback to spin wait
|
||||
while (m_cvx32 & 0xffffffff)
|
||||
{
|
||||
busy_wait();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const u64 wait_bit = result;
|
||||
const u64 lock_bit = wait_bit | (wait_bit << 32);
|
||||
|
||||
balanced_wait_until(m_cvx32, -1, [&](u64& cvx32, auto... ret) -> int
|
||||
{
|
||||
if ((cvx32 & wait_bit) == 0)
|
||||
{
|
||||
// Remove signal and unlock at once
|
||||
cvx32 &= ~lock_bit;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
cvx32 &= ~lock_bit;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
bool shared_cond::wait_all(shared_cond::shared_lock& lock) noexcept
|
||||
{
|
||||
AUDIT(lock.m_this == this);
|
||||
|
||||
if (lock.m_slot >= 32)
|
||||
{
|
||||
// Invalid argument, assume notified
|
||||
return true;
|
||||
}
|
||||
|
||||
const u64 wait_bit = c_wait << lock.m_slot;
|
||||
const u64 lock_bit = c_lock << lock.m_slot;
|
||||
|
||||
// Try to acquire waiting state only if there are other locks
|
||||
const auto [old_, not_alone] = m_cvx32.fetch_op([&](u64& cvx32)
|
||||
{
|
||||
// Check locking alone
|
||||
if (((cvx32 >> 32) & cvx32) == (lock_bit >> 32))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// c_lock -> c_wait, c_sig -> unlock
|
||||
cvx32 &= ~(lock_bit & ~wait_bit);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!not_alone)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set invalid slot to acknowledge unlocking
|
||||
lock.m_slot = 33;
|
||||
}
|
||||
|
||||
if ((old_ & wait_bit) == 0)
|
||||
{
|
||||
// Already signaled, return without waiting
|
||||
return true;
|
||||
}
|
||||
|
||||
balanced_wait_until(m_cvx32, -1, [&](u64& cvx32, auto... ret) -> int
|
||||
{
|
||||
if ((cvx32 & wait_bit) == 0)
|
||||
{
|
||||
// Remove signal and unlock at once
|
||||
cvx32 &= ~lock_bit;
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
cvx32 &= ~lock_bit;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shared_cond::notify_all(shared_cond::shared_lock& lock) noexcept
|
||||
{
|
||||
AUDIT(lock.m_this == this);
|
||||
|
||||
if (lock.m_slot >= 32)
|
||||
{
|
||||
// Invalid argument
|
||||
return false;
|
||||
}
|
||||
|
||||
const u64 slot_mask = c_sig << lock.m_slot;
|
||||
|
||||
auto [old, ok] = m_cvx32.fetch_op([&](u64& cvx32)
|
||||
{
|
||||
if (((cvx32 << 32) & cvx32) != slot_mask)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (const u64 sig_mask = cvx32 & 0xffffffff)
|
||||
{
|
||||
cvx32 &= (0xffffffffull << 32) & ~slot_mask;
|
||||
cvx32 |= (sig_mask << 32) & ~slot_mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
// Not an exclusive reader
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set invalid slot to acknowledge unlocking
|
||||
lock.m_slot = 34;
|
||||
|
||||
// Determine if some waiters need a syscall notification
|
||||
const u64 wait_mask = old & (~old >> 32);
|
||||
|
||||
if (UNLIKELY(!wait_mask))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
balanced_awaken<true>(m_cvx32, utils::popcnt32(wait_mask));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lf_queue_base::wait(u64 _timeout)
|
||||
{
|
||||
auto _old = m_head.compare_and_swap(0, 1);
|
||||
|
||||
if (_old)
|
||||
{
|
||||
verify("lf_queue concurrent wait" HERE), _old != 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return balanced_wait_until(m_head, _timeout, [](std::uintptr_t& head, auto... ret) -> int
|
||||
{
|
||||
if (head != 1)
|
||||
{
|
||||
return +1;
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(ret))
|
||||
{
|
||||
head = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
void lf_queue_base::imp_notify()
|
||||
{
|
||||
balanced_awaken(m_head, 1);
|
||||
// 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
|
||||
}
|
||||
|
||||
+3
-237
@@ -1,9 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include <shared_mutex>
|
||||
#include "asm.h"
|
||||
#include "Atomic.h"
|
||||
|
||||
// Lightweight condition variable
|
||||
class cond_variable
|
||||
@@ -11,8 +9,6 @@ class cond_variable
|
||||
// Internal waiter counter
|
||||
atomic_t<u32> m_value{0};
|
||||
|
||||
friend class notifier;
|
||||
|
||||
protected:
|
||||
// Internal waiting function
|
||||
bool imp_wait(u32 _old, u64 _timeout) noexcept;
|
||||
@@ -25,7 +21,7 @@ public:
|
||||
|
||||
// Intrusive wait algorithm for lockable objects
|
||||
template <typename T>
|
||||
bool wait(T& object, u64 usec_timeout = -1)
|
||||
explicit_bool_t wait(T& object, u64 usec_timeout = -1)
|
||||
{
|
||||
const u32 _old = m_value.fetch_add(1); // Increment waiter counter
|
||||
object.unlock();
|
||||
@@ -34,15 +30,6 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
// Unlock all specified objects but don't lock them again
|
||||
template <typename... Locks>
|
||||
bool wait_unlock(u64 usec_timeout, Locks&&... locks)
|
||||
{
|
||||
const u32 _old = m_value.fetch_add(1); // Increment waiter counter
|
||||
(..., std::forward<Locks>(locks).unlock());
|
||||
return imp_wait(_old, usec_timeout);
|
||||
}
|
||||
|
||||
// Wake one thread
|
||||
void notify_one() noexcept
|
||||
{
|
||||
@@ -57,228 +44,7 @@ public:
|
||||
{
|
||||
if (m_value)
|
||||
{
|
||||
imp_wake(65535);
|
||||
imp_wake(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr u64 max_timeout = u64{UINT32_MAX} / 1000 * 1000000;
|
||||
};
|
||||
|
||||
// Pair of a fake shared mutex (only limited shared locking) and a condition variable. Obsolete.
|
||||
class notifier
|
||||
{
|
||||
atomic_t<u32> m_counter{0};
|
||||
cond_variable m_cond;
|
||||
|
||||
bool imp_try_lock(u32 count);
|
||||
|
||||
void imp_unlock(u32 count);
|
||||
|
||||
u32 imp_notify(u32 count);
|
||||
|
||||
public:
|
||||
constexpr notifier() = default;
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
return imp_try_lock(max_readers);
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
imp_unlock(max_readers);
|
||||
}
|
||||
|
||||
bool try_lock_shared()
|
||||
{
|
||||
return imp_try_lock(1);
|
||||
}
|
||||
|
||||
void unlock_shared()
|
||||
{
|
||||
imp_unlock(1);
|
||||
}
|
||||
|
||||
bool wait(u64 usec_timeout = -1);
|
||||
|
||||
void notify_all()
|
||||
{
|
||||
if (m_counter)
|
||||
{
|
||||
imp_notify(-1);
|
||||
}
|
||||
|
||||
// Notify after imaginary "exclusive" lock+unlock
|
||||
m_cond.notify_all();
|
||||
}
|
||||
|
||||
void notify_one()
|
||||
{
|
||||
// TODO
|
||||
if (m_counter)
|
||||
{
|
||||
if (imp_notify(1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_cond.notify_one();
|
||||
}
|
||||
|
||||
static constexpr u32 max_readers = 0x7f;
|
||||
};
|
||||
|
||||
// Condition variable fused with a pseudo-mutex which is never supposed to be locked concurrently.
|
||||
class unique_cond
|
||||
{
|
||||
enum : u32
|
||||
{
|
||||
c_wait = 1,
|
||||
c_lock = 2,
|
||||
c_sig = 3,
|
||||
};
|
||||
|
||||
atomic_t<u32> m_value{0};
|
||||
|
||||
bool imp_wait(u64 _timeout) noexcept;
|
||||
void imp_notify() noexcept;
|
||||
|
||||
public:
|
||||
constexpr unique_cond() = default;
|
||||
|
||||
void lock() noexcept
|
||||
{
|
||||
// Shouldn't be locked by more than one thread concurrently
|
||||
while (UNLIKELY(!m_value.compare_and_swap_test(0, c_lock)))
|
||||
;
|
||||
}
|
||||
|
||||
void unlock() noexcept
|
||||
{
|
||||
m_value = 0;
|
||||
}
|
||||
|
||||
bool wait(std::unique_lock<unique_cond>& lock, u64 usec_timeout = -1) noexcept
|
||||
{
|
||||
AUDIT(lock.owns_lock());
|
||||
AUDIT(lock.mutex() == this);
|
||||
return imp_wait(usec_timeout);
|
||||
}
|
||||
|
||||
void notify() noexcept
|
||||
{
|
||||
// Early exit if notification is not required
|
||||
if (LIKELY(!m_value))
|
||||
return;
|
||||
|
||||
imp_notify();
|
||||
}
|
||||
};
|
||||
|
||||
// Condition variable fused with a pseudo-mutex supporting only reader locks (up to 32 readers).
|
||||
class shared_cond
|
||||
{
|
||||
// For information, shouldn't modify
|
||||
enum : u64
|
||||
{
|
||||
// Wait bit is aligned for compatibility with 32-bit futex.
|
||||
c_wait = 1,
|
||||
c_sig = 1ull << 32,
|
||||
c_lock = 1ull << 32 | 1,
|
||||
};
|
||||
|
||||
// Split in 32-bit parts for convenient bit combining
|
||||
atomic_t<u64> m_cvx32{0};
|
||||
|
||||
class shared_lock
|
||||
{
|
||||
shared_cond* m_this;
|
||||
u32 m_slot;
|
||||
|
||||
friend class shared_cond;
|
||||
|
||||
public:
|
||||
shared_lock(shared_cond* _this) noexcept
|
||||
: m_this(_this)
|
||||
{
|
||||
// Lock and remember obtained slot index
|
||||
m_slot = m_this->m_cvx32.atomic_op([](u64& cvx32)
|
||||
{
|
||||
// Combine used bits and invert to find least significant bit unused
|
||||
const u32 slot = static_cast<u32>(utils::cnttz64(~((cvx32 & 0xffffffff) | (cvx32 >> 32)), true));
|
||||
|
||||
// Set lock bits (does nothing if all slots are used)
|
||||
const u64 bit = (1ull << slot) & 0xffffffff;
|
||||
cvx32 |= bit | (bit << 32);
|
||||
return slot;
|
||||
});
|
||||
}
|
||||
|
||||
shared_lock(const shared_lock&) = delete;
|
||||
|
||||
shared_lock(shared_lock&& rhs)
|
||||
: m_this(rhs.m_this)
|
||||
, m_slot(rhs.m_slot)
|
||||
{
|
||||
rhs.m_slot = 32;
|
||||
}
|
||||
|
||||
shared_lock& operator=(const shared_lock&) = delete;
|
||||
|
||||
~shared_lock()
|
||||
{
|
||||
// Clear the slot (does nothing if all slots are used)
|
||||
const u64 bit = (1ull << m_slot) & 0xffffffff;
|
||||
m_this->m_cvx32 &= ~(bit | (bit << 32));
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
// Check success
|
||||
return m_slot < 32;
|
||||
}
|
||||
|
||||
bool wait(u64 usec_timeout = -1) const noexcept
|
||||
{
|
||||
return m_this->wait(*this, usec_timeout);
|
||||
}
|
||||
};
|
||||
|
||||
bool imp_wait(u32 slot, u64 _timeout) noexcept;
|
||||
void imp_notify() noexcept;
|
||||
|
||||
public:
|
||||
constexpr shared_cond() = default;
|
||||
|
||||
shared_lock try_shared_lock() noexcept
|
||||
{
|
||||
return shared_lock(this);
|
||||
}
|
||||
|
||||
u32 count() const noexcept
|
||||
{
|
||||
const u64 cvx32 = m_cvx32;
|
||||
return utils::popcnt32(static_cast<u32>(cvx32 | (cvx32 >> 32)));
|
||||
}
|
||||
|
||||
bool wait(shared_lock const& lock, u64 usec_timeout = -1) noexcept
|
||||
{
|
||||
AUDIT(lock.m_this == this);
|
||||
return imp_wait(lock.m_slot, usec_timeout);
|
||||
}
|
||||
|
||||
void wait_all() noexcept;
|
||||
|
||||
bool wait_all(shared_lock& lock) noexcept;
|
||||
|
||||
void notify_all() noexcept
|
||||
{
|
||||
if (LIKELY(!m_cvx32))
|
||||
return;
|
||||
|
||||
imp_notify();
|
||||
}
|
||||
|
||||
bool notify_all(shared_lock& lock) noexcept;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
Copyright (C) 2011-2012 Andrzej Krzemienski
|
||||
|
||||
Distributed under the Boost Software License, Version 1.0
|
||||
(see accompanying file LICENSE_1_0.txt or a copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
The idea and interface is based on Boost.Optional library
|
||||
authored by Fernando Luis Cacciola Carballal
|
||||
|
||||
Home at https://github.com/akrzemi1/Optional
|
||||
@@ -1,44 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
namespace date_time
|
||||
{
|
||||
static inline tm get_time(time_t* _time)
|
||||
{
|
||||
tm buf;
|
||||
time_t t = time(_time);
|
||||
#ifdef _MSC_VER
|
||||
localtime_s(&buf, &t);
|
||||
#else
|
||||
buf = *localtime(&t);
|
||||
#endif
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline std::string current_time()
|
||||
{
|
||||
char str[80];
|
||||
tm now = get_time(0);
|
||||
strftime(str, sizeof(str), "%c", &now);
|
||||
return str;
|
||||
}
|
||||
|
||||
template<char separator = 0>
|
||||
static inline std::string current_time_narrow()
|
||||
{
|
||||
char str[80];
|
||||
tm now = get_time(0);
|
||||
|
||||
std::string parse_buf;
|
||||
|
||||
if constexpr(separator != 0)
|
||||
parse_buf = std::string("%Y") + separator + "%m" + separator + "%d" + separator + "%H" + separator + "%M" + separator + "%S";
|
||||
else
|
||||
parse_buf = "%Y%m%d%H%M%S";
|
||||
|
||||
strftime(str, sizeof(str), parse_buf.c_str(), &now);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "dynamic_library.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -50,7 +50,7 @@ namespace utils
|
||||
|
||||
bool dynamic_library::loaded() const
|
||||
{
|
||||
return m_handle != nullptr;
|
||||
return !m_handle;
|
||||
}
|
||||
|
||||
dynamic_library::operator bool() const
|
||||
|
||||
@@ -63,30 +63,18 @@ namespace utils
|
||||
{
|
||||
}
|
||||
|
||||
void init()
|
||||
// Caller
|
||||
R operator()(Args... args)
|
||||
{
|
||||
if (!ptr)
|
||||
{
|
||||
// TODO: atomic
|
||||
ptr = reinterpret_cast<R(*)(Args...)>(get_proc_address(lib, name));
|
||||
}
|
||||
}
|
||||
|
||||
operator bool()
|
||||
{
|
||||
init();
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Caller
|
||||
R operator()(Args... args)
|
||||
{
|
||||
init();
|
||||
|
||||
return ptr(args...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#define DYNAMIC_IMPORT(lib, name, ...) inline utils::dynamic_import<__VA_ARGS__> name(lib, #name);
|
||||
#define DYNAMIC_IMPORT(lib, name, ...) static utils::dynamic_import<__VA_ARGS__> name(lib, #name);
|
||||
|
||||
+5
-7
@@ -4,7 +4,7 @@
|
||||
#include <deque>
|
||||
#include <list>
|
||||
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
|
||||
template <typename T, T Mod = T::__state_enum_max, typename Under = std::underlying_type_t<T>>
|
||||
T operator ++(T& value, int)
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
}
|
||||
|
||||
// Conditionally set state (optimized)
|
||||
bool state_test_and_set(T expected, T state)
|
||||
explicit_bool_t state_test_and_set(T expected, T state)
|
||||
{
|
||||
if (m_value == expected && m_value.compare_and_swap_test(expected, state))
|
||||
{
|
||||
@@ -83,7 +83,7 @@ public:
|
||||
}
|
||||
|
||||
// Conditionally set state (list version)
|
||||
bool state_test_and_set(std::initializer_list<T> expected, T state)
|
||||
explicit_bool_t state_test_and_set(std::initializer_list<T> expected, T state)
|
||||
{
|
||||
T _old;
|
||||
|
||||
@@ -111,10 +111,9 @@ public:
|
||||
// Unconditionally set next state
|
||||
void state_next()
|
||||
{
|
||||
T _old, state = m_value.atomic_op([&](T& value)
|
||||
T _old, state = m_value.op_fetch([&](T& value)
|
||||
{
|
||||
_old = value++;
|
||||
return value;
|
||||
});
|
||||
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(_old);
|
||||
@@ -123,10 +122,9 @@ public:
|
||||
// Unconditionally set previous state
|
||||
void state_prev()
|
||||
{
|
||||
T _old, state = m_value.atomic_op([&](T& value)
|
||||
T _old, state = m_value.op_fetch([&](T& value)
|
||||
{
|
||||
_old = value--;
|
||||
return value;
|
||||
});
|
||||
|
||||
(static_cast<CRT*>(this)->*transition_get(state))(_old);
|
||||
|
||||
+8
-38
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
@@ -673,16 +673,6 @@ struct area_base
|
||||
return{ x1, y1, x2 - x1, y2 - y1 };
|
||||
}
|
||||
|
||||
constexpr T width() const
|
||||
{
|
||||
return (x1 < x2) ? (x2 - x1) : (x1 - x2);
|
||||
}
|
||||
|
||||
constexpr T height() const
|
||||
{
|
||||
return (y1 < y2) ? (y2 - y1) : (y1 - y2);
|
||||
}
|
||||
|
||||
void flip_vertical()
|
||||
{
|
||||
T _y = y1; y1 = y2; y2 = _y;
|
||||
@@ -703,11 +693,6 @@ struct area_base
|
||||
return{ x2, y1, x1, y2 };
|
||||
}
|
||||
|
||||
constexpr bool is_flipped() const
|
||||
{
|
||||
return (x1 > x2 || y1 > y2);
|
||||
}
|
||||
|
||||
constexpr bool operator == (const area_base& rhs) const
|
||||
{
|
||||
return x1 == rhs.x1 && x2 == rhs.x2 && y1 == rhs.y1 && y2 == rhs.y2;
|
||||
@@ -746,9 +731,9 @@ struct area_base
|
||||
{
|
||||
return{ x1 * size.width, y1 * size.height, x2 * size.width, y2 * size.height };
|
||||
}
|
||||
constexpr area_base operator * (const f32& value) const
|
||||
constexpr area_base operator * (const T& value) const
|
||||
{
|
||||
return{ (T)(x1 * value), (T)(y1 * value), (T)(x2 * value), (T)(y2 * value) };
|
||||
return{ x1 * value, y1 * value, x2 * value, y2 * value };
|
||||
}
|
||||
|
||||
template<typename NT>
|
||||
@@ -841,7 +826,7 @@ struct color4_base
|
||||
T xyzw[4];
|
||||
};
|
||||
|
||||
constexpr color4_base()
|
||||
color4_base()
|
||||
: x{}
|
||||
, y{}
|
||||
, z{}
|
||||
@@ -849,7 +834,7 @@ struct color4_base
|
||||
{
|
||||
}
|
||||
|
||||
constexpr color4_base(T x, T y = {}, T z = {}, T w = {})
|
||||
color4_base(T x, T y = {}, T z = {}, T w = {})
|
||||
: x(x)
|
||||
, y(y)
|
||||
, z(z)
|
||||
@@ -857,18 +842,18 @@ struct color4_base
|
||||
{
|
||||
}
|
||||
|
||||
constexpr bool operator == (const color4_base& rhs) const
|
||||
bool operator == (const color4_base& rhs) const
|
||||
{
|
||||
return r == rhs.r && g == rhs.g && b == rhs.b && a == rhs.a;
|
||||
}
|
||||
|
||||
constexpr bool operator != (const color4_base& rhs) const
|
||||
bool operator != (const color4_base& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
template<typename NT>
|
||||
constexpr operator color4_base<NT>() const
|
||||
operator color4_base<NT>() const
|
||||
{
|
||||
return{ (NT)x, (NT)y, (NT)z, (NT)w };
|
||||
}
|
||||
@@ -991,77 +976,62 @@ struct color1_base
|
||||
};
|
||||
|
||||
//specializations
|
||||
using positionu = position_base<unsigned int>;
|
||||
using positioni = position_base<int>;
|
||||
using positionf = position_base<float>;
|
||||
using positiond = position_base<double>;
|
||||
|
||||
using coordu = coord_base<unsigned int>;
|
||||
using coordi = coord_base<int>;
|
||||
using coordf = coord_base<float>;
|
||||
using coordd = coord_base<double>;
|
||||
|
||||
using areau = area_base<unsigned int>;
|
||||
using areai = area_base<int>;
|
||||
using areaf = area_base<float>;
|
||||
using aread = area_base<double>;
|
||||
|
||||
using position1u = position1_base<unsigned int>;
|
||||
using position1i = position1_base<int>;
|
||||
using position1f = position1_base<float>;
|
||||
using position1d = position1_base<double>;
|
||||
|
||||
using position2u = position2_base<unsigned int>;
|
||||
using position2i = position2_base<int>;
|
||||
using position2f = position2_base<float>;
|
||||
using position2d = position2_base<double>;
|
||||
|
||||
using position3u = position3_base<unsigned int>;
|
||||
using position3i = position3_base<int>;
|
||||
using position3f = position3_base<float>;
|
||||
using position3d = position3_base<double>;
|
||||
|
||||
using position4u = position4_base<unsigned int>;
|
||||
using position4i = position4_base<int>;
|
||||
using position4f = position4_base<float>;
|
||||
using position4d = position4_base<double>;
|
||||
|
||||
using size2u = size2_base<unsigned int>;
|
||||
using size2i = size2_base<int>;
|
||||
using size2f = size2_base<float>;
|
||||
using size2d = size2_base<double>;
|
||||
|
||||
using sizeu = size2u;
|
||||
using sizei = size2i;
|
||||
using sizef = size2f;
|
||||
using sized = size2d;
|
||||
|
||||
using size3u = size3_base<unsigned int>;
|
||||
using size3i = size3_base<int>;
|
||||
using size3f = size3_base<float>;
|
||||
using size3d = size3_base<double>;
|
||||
|
||||
using coord3u = coord3_base<unsigned int>;
|
||||
using coord3i = coord3_base<int>;
|
||||
using coord3f = coord3_base<float>;
|
||||
using coord3d = coord3_base<double>;
|
||||
|
||||
using color4u = color4_base<unsigned int>;
|
||||
using color4i = color4_base<int>;
|
||||
using color4f = color4_base<float>;
|
||||
using color4d = color4_base<double>;
|
||||
|
||||
using color3u = color3_base<unsigned int>;
|
||||
using color3i = color3_base<int>;
|
||||
using color3f = color3_base<float>;
|
||||
using color3d = color3_base<double>;
|
||||
|
||||
using color2u = color2_base<unsigned int>;
|
||||
using color2i = color2_base<int>;
|
||||
using color2f = color2_base<float>;
|
||||
using color2d = color2_base<double>;
|
||||
|
||||
using color1u = color1_base<unsigned int>;
|
||||
using color1i = color1_base<int>;
|
||||
using color1f = color1_base<float>;
|
||||
using color1d = color1_base<double>;
|
||||
|
||||
@@ -13,10 +13,9 @@ rem // A copy of the GPL 2.0 should have been included with the program.
|
||||
rem // If not, see http://www.gnu.org/licenses/
|
||||
|
||||
rem // Official git repository and contact information can be found at
|
||||
rem // https://github.com/RPCS3/rpcs3 and https://rpcs3.net/.
|
||||
rem // https://github.com/RPCS3/rpcs3 and http://rpcs3.net/.
|
||||
|
||||
setlocal ENABLEDELAYEDEXPANSION
|
||||
setlocal ENABLEEXTENSIONS
|
||||
|
||||
set GIT_VERSION_FILE=%~p0..\rpcs3\git-version.h
|
||||
if not defined GIT (
|
||||
@@ -48,40 +47,14 @@ if errorlevel 1 (
|
||||
echo // This is a generated file. > "%GIT_VERSION_FILE%"
|
||||
echo. >> "%GIT_VERSION_FILE%"
|
||||
echo #define RPCS3_GIT_VERSION "unknown" >> "%GIT_VERSION_FILE%"
|
||||
echo #define RPCS3_GIT_BRANCH "unknown" >> "%GIT_VERSION_FILE%"
|
||||
echo. >> "%GIT_VERSION_FILE%"
|
||||
echo // If you don't want this file to update/recompile, change to 1. >> "%GIT_VERSION_FILE%"
|
||||
echo #define RPCS3_GIT_VERSION_NO_UPDATE 0 >> "%GIT_VERSION_FILE%"
|
||||
goto done
|
||||
)
|
||||
|
||||
rem // Get commit count from (unshallowed) HEAD
|
||||
for /F %%I IN ('call %GIT% rev-list HEAD --count') do set COMMIT_COUNT=%%I
|
||||
|
||||
rem // If we're in AppVeyor, building a non-master, pull request artifact
|
||||
if defined APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH (
|
||||
|
||||
if "%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%"=="master" (
|
||||
rem // If pull request comes from a master branch, GIT_BRANCH = username/branch in order to distinguish from upstream/master
|
||||
for /f "tokens=1* delims=/" %%a in ("%APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME%") do set user=%%a
|
||||
set "GIT_BRANCH=!user!/%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%"
|
||||
) else (
|
||||
rem // Otherwise, GIT_BRANCH=branch
|
||||
set GIT_BRANCH=%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%
|
||||
)
|
||||
|
||||
rem // Make GIT_VERSION the last commit (shortened); Don't include commit count on non-master builds
|
||||
for /F %%I IN ('call %GIT% rev-parse --short^=8 HEAD') do set GIT_VERSION=%%I
|
||||
|
||||
) else (
|
||||
rem // Get last commit (shortened) and concat after commit count in GIT_VERSION
|
||||
for /F %%I IN ('call %GIT% rev-parse --short^=8 HEAD') do set GIT_VERSION=%COMMIT_COUNT%-%%I
|
||||
|
||||
for /F %%I IN ('call %GIT% rev-parse --abbrev-ref HEAD') do set GIT_BRANCH=%%I
|
||||
)
|
||||
|
||||
rem // Echo obtained GIT_VERSION for debug purposes if needed
|
||||
echo %GIT_VERSION%
|
||||
for /F %%I IN ('call %GIT% rev-list HEAD --count') do set GIT_VERSION=%%I
|
||||
for /F %%I IN ('call %GIT% rev-parse --short HEAD') do set GIT_VERSION=%GIT_VERSION%-%%I
|
||||
|
||||
rem // Don't modify the file if it already has the current version.
|
||||
if exist "%GIT_VERSION_FILE%" (
|
||||
@@ -94,7 +67,6 @@ if exist "%GIT_VERSION_FILE%" (
|
||||
echo // This is a generated file. > "%GIT_VERSION_FILE%"
|
||||
echo. >> "%GIT_VERSION_FILE%"
|
||||
echo #define RPCS3_GIT_VERSION "%GIT_VERSION%" >> "%GIT_VERSION_FILE%"
|
||||
echo #define RPCS3_GIT_BRANCH ^"%GIT_BRANCH%^" >> "%GIT_VERSION_FILE%"
|
||||
echo. >> "%GIT_VERSION_FILE%"
|
||||
echo // If you don't want this file to update/recompile, change to 1. >> "%GIT_VERSION_FILE%"
|
||||
echo #define RPCS3_GIT_VERSION_NO_UPDATE 0 >> "%GIT_VERSION_FILE%"
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/types.h"
|
||||
|
||||
namespace rpcs3
|
||||
{
|
||||
template<typename T>
|
||||
static size_t hash_base(T value)
|
||||
{
|
||||
return static_cast<size_t>(value);
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static size_t hash_struct_base(const T& value)
|
||||
{
|
||||
// FNV 64-bit
|
||||
size_t result = 14695981039346656037ull;
|
||||
const U *bits = reinterpret_cast<const U*>(&value);
|
||||
|
||||
for (size_t n = 0; n < (sizeof(T) / sizeof(U)); ++n)
|
||||
{
|
||||
result ^= bits[n];
|
||||
result *= 1099511628211ull;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static size_t hash_struct(const T& value)
|
||||
{
|
||||
static constexpr auto block_sz = sizeof(T);
|
||||
|
||||
if constexpr ((block_sz & 0x7) == 0)
|
||||
{
|
||||
return hash_struct_base<T, u64>(value);
|
||||
}
|
||||
|
||||
if constexpr ((block_sz & 0x3) == 0)
|
||||
{
|
||||
return hash_struct_base<T, u32>(value);
|
||||
}
|
||||
|
||||
if constexpr ((block_sz & 0x1) == 0)
|
||||
{
|
||||
return hash_struct_base<T, u16>(value);
|
||||
}
|
||||
|
||||
return hash_struct_base<T, u8>(value);
|
||||
}
|
||||
}
|
||||
+131
-279
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
|
||||
//! Simple sizeless array base for concurrent access. Cannot shrink, only growths automatically.
|
||||
//! There is no way to know the current size. The smaller index is, the faster it's accessed.
|
||||
//!
|
||||
//!
|
||||
//! T is the type of elements. Currently, default constructor of T shall be constexpr.
|
||||
//! N is initial element count, available without any memory allocation and only stored contiguously.
|
||||
template <typename T, std::size_t N>
|
||||
template<typename T, std::size_t N>
|
||||
class lf_array
|
||||
{
|
||||
// Data (default-initialized)
|
||||
@@ -68,13 +68,13 @@ public:
|
||||
// Get current "push" position
|
||||
u32 size() const
|
||||
{
|
||||
return m_ctrl.load().push;
|
||||
return reinterpret_cast<const atomic_t<u32>&>(m_ctrl).load(); // Hack
|
||||
}
|
||||
|
||||
|
||||
// Acquire the place for one or more elements.
|
||||
u32 push_begin(u32 count = 1)
|
||||
{
|
||||
return std::bit_cast<atomic_t<u64>*>(&m_ctrl)->fetch_add(count); // Hack
|
||||
return reinterpret_cast<atomic_t<u32>&>(m_ctrl).fetch_add(count); // Hack
|
||||
}
|
||||
|
||||
// Get current "pop" position
|
||||
@@ -130,7 +130,7 @@ public:
|
||||
{
|
||||
return m_default_key_data;
|
||||
}
|
||||
|
||||
|
||||
// Calculate hash and array position
|
||||
for (std::size_t pos = Hash{}(key) % Size;; pos += Size)
|
||||
{
|
||||
@@ -146,333 +146,185 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Helper type, linked list element
|
||||
template <typename T>
|
||||
class lf_queue_item final
|
||||
// Fixed-size single-producer single-consumer queue
|
||||
template <typename T, std::uint32_t N>
|
||||
class lf_spsc
|
||||
{
|
||||
lf_queue_item* m_link = nullptr;
|
||||
// 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");
|
||||
|
||||
T m_data;
|
||||
|
||||
template <typename U>
|
||||
friend class lf_queue_iterator;
|
||||
|
||||
template <typename U>
|
||||
friend class lf_queue_slice;
|
||||
|
||||
template <typename U>
|
||||
friend class lf_queue;
|
||||
|
||||
constexpr lf_queue_item() = default;
|
||||
|
||||
template <typename... Args>
|
||||
constexpr lf_queue_item(lf_queue_item* link, Args&&... args)
|
||||
: m_link(link)
|
||||
, m_data(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
protected:
|
||||
volatile std::uint32_t m_push{0};
|
||||
volatile std::uint32_t m_pop{0};
|
||||
T m_data[N]{};
|
||||
|
||||
public:
|
||||
lf_queue_item(const lf_queue_item&) = delete;
|
||||
constexpr lf_spsc() = default;
|
||||
|
||||
lf_queue_item& operator=(const lf_queue_item&) = delete;
|
||||
|
||||
~lf_queue_item()
|
||||
// Try to push (producer only)
|
||||
template <typename T2>
|
||||
bool try_push(T2&& data)
|
||||
{
|
||||
for (lf_queue_item* ptr = m_link; ptr;)
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop >= N)
|
||||
{
|
||||
delete std::exchange(ptr, std::exchange(ptr->m_link, nullptr));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Forward iterator: non-owning pointer to the list element in lf_queue_slice<>
|
||||
template <typename T>
|
||||
class lf_queue_iterator
|
||||
{
|
||||
lf_queue_item<T>* m_ptr = nullptr;
|
||||
|
||||
template <typename U>
|
||||
friend class lf_queue_slice;
|
||||
|
||||
public:
|
||||
constexpr lf_queue_iterator() = default;
|
||||
|
||||
bool operator ==(const lf_queue_iterator& rhs) const
|
||||
{
|
||||
return m_ptr == rhs.m_ptr;
|
||||
}
|
||||
|
||||
bool operator !=(const lf_queue_iterator& rhs) const
|
||||
{
|
||||
return m_ptr != rhs.m_ptr;
|
||||
}
|
||||
|
||||
T& operator *() const
|
||||
{
|
||||
return m_ptr->m_data;
|
||||
}
|
||||
|
||||
T* operator ->() const
|
||||
{
|
||||
return &m_ptr->m_data;
|
||||
}
|
||||
|
||||
lf_queue_iterator& operator ++()
|
||||
{
|
||||
m_ptr = m_ptr->m_link;
|
||||
return *this;
|
||||
}
|
||||
|
||||
lf_queue_iterator operator ++(int)
|
||||
{
|
||||
lf_queue_iterator result;
|
||||
result.m_ptr = m_ptr;
|
||||
m_ptr = m_ptr->m_link;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Owning pointer to the linked list taken from the lf_queue<>
|
||||
template <typename T>
|
||||
class lf_queue_slice
|
||||
{
|
||||
lf_queue_item<T>* m_head = nullptr;
|
||||
|
||||
template <typename U>
|
||||
friend class lf_queue;
|
||||
|
||||
public:
|
||||
constexpr lf_queue_slice() = default;
|
||||
|
||||
lf_queue_slice(const lf_queue_slice&) = delete;
|
||||
|
||||
lf_queue_slice(lf_queue_slice&& r) noexcept
|
||||
: m_head(r.m_head)
|
||||
{
|
||||
r.m_head = nullptr;
|
||||
}
|
||||
|
||||
lf_queue_slice& operator =(const lf_queue_slice&) = delete;
|
||||
|
||||
lf_queue_slice& operator =(lf_queue_slice&& r) noexcept
|
||||
{
|
||||
if (this != &r)
|
||||
{
|
||||
delete m_head;
|
||||
m_head = r.m_head;
|
||||
r.m_head = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return *this;
|
||||
_mm_lfence();
|
||||
m_data[pos % N] = std::forward<T2>(data);
|
||||
_mm_sfence();
|
||||
m_push = pos + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
~lf_queue_slice()
|
||||
// Try to get push pointer (producer only)
|
||||
operator T*()
|
||||
{
|
||||
delete m_head;
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop >= N)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_mm_lfence();
|
||||
return m_data + (pos % N);
|
||||
}
|
||||
|
||||
T& operator *() const
|
||||
// Increment push counter (producer only)
|
||||
void end_push()
|
||||
{
|
||||
return m_head->m_data;
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
if (pos - m_pop < N)
|
||||
{
|
||||
_mm_sfence();
|
||||
m_push = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
T* operator ->() const
|
||||
// Unsafe access
|
||||
T& get_push(std::size_t i)
|
||||
{
|
||||
return &m_head->m_data;
|
||||
_mm_lfence();
|
||||
return m_data[(m_push + i) % N];
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
// Try to pop (consumer only)
|
||||
template <typename T2>
|
||||
bool try_pop(T2& out)
|
||||
{
|
||||
return m_head != nullptr;
|
||||
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;
|
||||
}
|
||||
|
||||
T* get() const
|
||||
// Increment pop counter (consumer only)
|
||||
void end_pop()
|
||||
{
|
||||
return m_head ? &m_head->m_data : nullptr;
|
||||
const std::uint32_t pos = m_pop;
|
||||
|
||||
if (m_push - pos > 0)
|
||||
{
|
||||
_mm_sfence();
|
||||
m_pop = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
lf_queue_iterator<T> begin() const
|
||||
// Get size (consumer only)
|
||||
std::uint32_t size() const
|
||||
{
|
||||
lf_queue_iterator<T> result;
|
||||
result.m_ptr = m_head;
|
||||
return result;
|
||||
return m_push - m_pop;
|
||||
}
|
||||
|
||||
lf_queue_iterator<T> end() const
|
||||
// Direct access (consumer only)
|
||||
T& operator [](std::size_t i)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
lf_queue_slice& pop_front()
|
||||
{
|
||||
delete std::exchange(m_head, std::exchange(m_head->m_link, nullptr));
|
||||
return *this;
|
||||
_mm_lfence();
|
||||
return m_data[(m_pop + i) % N];
|
||||
}
|
||||
};
|
||||
|
||||
class lf_queue_base
|
||||
// Fixed-size multi-producer single-consumer queue
|
||||
template <typename T, std::uint32_t N>
|
||||
class lf_mpsc : lf_spsc<T, N>
|
||||
{
|
||||
protected:
|
||||
atomic_t<std::uintptr_t> m_head = 0;
|
||||
using lf_spsc<T, N>::m_push;
|
||||
using lf_spsc<T, N>::m_pop;
|
||||
using lf_spsc<T, N>::m_data;
|
||||
|
||||
void imp_notify();
|
||||
|
||||
public:
|
||||
// Wait for new elements pushed, no other thread shall call wait() or pop_all() simultaneously
|
||||
bool wait(u64 usec_timeout = -1);
|
||||
};
|
||||
|
||||
// Linked list-based multi-producer queue (the consumer drains the whole queue at once)
|
||||
template <typename T>
|
||||
class lf_queue : public lf_queue_base
|
||||
{
|
||||
using lf_queue_base::m_head;
|
||||
|
||||
// Extract all elements and reverse element order (FILO to FIFO)
|
||||
lf_queue_item<T>* reverse() noexcept
|
||||
enum : std::uint64_t
|
||||
{
|
||||
if (auto* head = m_head.load() ? reinterpret_cast<lf_queue_item<T>*>(m_head.exchange(0)) : nullptr)
|
||||
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)
|
||||
{
|
||||
if (auto* prev = head->m_link)
|
||||
{
|
||||
head->m_link = nullptr;
|
||||
|
||||
do
|
||||
{
|
||||
auto* pprev = prev->m_link;
|
||||
prev->m_link = head;
|
||||
head = std::exchange(prev, pprev);
|
||||
}
|
||||
while (prev);
|
||||
}
|
||||
|
||||
return head;
|
||||
_mm_sfence();
|
||||
m_push += value % c_rel;
|
||||
m_lock.compare_and_swap_test(value, 0);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr lf_queue() = default;
|
||||
constexpr lf_mpsc() = default;
|
||||
|
||||
~lf_queue()
|
||||
// Try to get push pointer
|
||||
operator T*()
|
||||
{
|
||||
delete reinterpret_cast<lf_queue_item<T>*>(m_head.load());
|
||||
}
|
||||
const std::uint64_t old = m_lock.fetch_add(c_ack);
|
||||
const std::uint32_t pos = m_push;
|
||||
|
||||
template <typename... Args>
|
||||
void push(Args&&... args)
|
||||
{
|
||||
auto _old = m_head.load();
|
||||
auto* item = new lf_queue_item<T>(_old & 1 ? nullptr : reinterpret_cast<lf_queue_item<T>*>(_old), std::forward<Args>(args)...);
|
||||
|
||||
while (!m_head.compare_exchange(_old, reinterpret_cast<std::uint64_t>(item)))
|
||||
if (old % N >= N || pos - m_pop >= N - (old % N))
|
||||
{
|
||||
item->m_link = _old & 1 ? nullptr : reinterpret_cast<lf_queue_item<T>*>(_old);
|
||||
release(m_lock.sub_fetch(c_ack));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (_old & 1)
|
||||
{
|
||||
lf_queue_base::imp_notify();
|
||||
}
|
||||
return m_data + ((pos + old) % N);
|
||||
}
|
||||
|
||||
// Withdraw the list, supports range-for loop: for (auto&& x : y.pop_all()) ...
|
||||
lf_queue_slice<T> pop_all()
|
||||
// Increment push counter (producer only)
|
||||
void end_push()
|
||||
{
|
||||
lf_queue_slice<T> result;
|
||||
result.m_head = reverse();
|
||||
return result;
|
||||
release(m_lock.add_fetch(c_rel));
|
||||
}
|
||||
|
||||
// Apply func(data) to each element, return the total length
|
||||
template <typename F>
|
||||
std::size_t apply(F func)
|
||||
// Try to push
|
||||
template <typename T2>
|
||||
bool try_push(T2&& data)
|
||||
{
|
||||
std::size_t count = 0;
|
||||
|
||||
for (auto slice = pop_all(); slice; slice.pop_front())
|
||||
if (T* ptr = *this)
|
||||
{
|
||||
std::invoke(func, *slice);
|
||||
*ptr = std::forward<T2>(data);
|
||||
end_push();
|
||||
return true;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
// Assignable lock-free thread-safe value of any type (memory-inefficient)
|
||||
template <typename T>
|
||||
class lf_value final
|
||||
{
|
||||
atomic_t<lf_value*> m_head;
|
||||
|
||||
T m_data;
|
||||
|
||||
public:
|
||||
template <typename... Args>
|
||||
explicit constexpr lf_value(Args&&... args)
|
||||
: m_head(this)
|
||||
, m_data(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
~lf_value()
|
||||
{
|
||||
// All values are kept in the queue until the end
|
||||
for (lf_value* ptr = m_head.load(); ptr != this;)
|
||||
{
|
||||
delete std::exchange(ptr, std::exchange(ptr->m_head.raw(), ptr));
|
||||
}
|
||||
}
|
||||
|
||||
// Get current head, allows to inspect old values
|
||||
[[nodiscard]] const lf_value* head() const
|
||||
{
|
||||
return m_head.load();
|
||||
}
|
||||
|
||||
// Inspect the initial (oldest) value
|
||||
[[nodiscard]] const T& first() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
[[nodiscard]] const T& get() const
|
||||
{
|
||||
return m_head.load()->m_data;
|
||||
}
|
||||
|
||||
[[nodiscard]] operator const T&() const
|
||||
{
|
||||
return m_head.load()->m_data;
|
||||
}
|
||||
|
||||
// Construct new value in-place
|
||||
template <typename... Args>
|
||||
const T& assign(Args&&... args)
|
||||
{
|
||||
lf_value* val = new lf_value(std::forward<Args>(args)...);
|
||||
lf_value* old = m_head.load();
|
||||
|
||||
do
|
||||
{
|
||||
val->m_head = old;
|
||||
}
|
||||
while (!m_head.compare_exchange(old, val));
|
||||
|
||||
return val->m_data;
|
||||
}
|
||||
|
||||
// Copy-assign new value
|
||||
const T& operator =(const T& value)
|
||||
{
|
||||
return assign(value);
|
||||
}
|
||||
|
||||
// Move-assign new value
|
||||
const T& operator =(T&& value)
|
||||
{
|
||||
return assign(std::move(value));
|
||||
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 [];
|
||||
};
|
||||
|
||||
+186
-192
@@ -1,258 +1,252 @@
|
||||
#include "mutex.h"
|
||||
#include "sync.h"
|
||||
|
||||
void shared_mutex::imp_lock_shared(u32 val)
|
||||
#include <limits.h>
|
||||
|
||||
void shared_mutex::imp_lock_shared(s64 _old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), val < c_err;
|
||||
verify("shared_mutex overflow" HERE), _old <= c_max;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
if (try_lock_shared())
|
||||
const s64 value = m_value.load();
|
||||
|
||||
if (value >= c_min && m_value.compare_and_swap_test(value, value - c_min))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire writer lock and downgrade
|
||||
const u32 old = m_value.fetch_add(c_one);
|
||||
#ifdef _WIN32
|
||||
// Acquire writer lock
|
||||
imp_wait(m_value.load());
|
||||
|
||||
if (old == 0)
|
||||
// Convert to reader lock
|
||||
s64 value = m_value.fetch_add(c_one - c_min);
|
||||
|
||||
// Proceed exclusively
|
||||
return;
|
||||
|
||||
if (value != 0)
|
||||
{
|
||||
lock_downgrade();
|
||||
return;
|
||||
imp_unlock(value);
|
||||
}
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
|
||||
imp_wait();
|
||||
lock_downgrade();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock_shared(u32 old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), old - 1 < c_err;
|
||||
|
||||
// Check reader count, notify the writer if necessary
|
||||
if ((old - 1) % c_one == 0)
|
||||
// Wait as a reader if necessary
|
||||
if (value + c_one - c_min < 0)
|
||||
{
|
||||
imp_signal();
|
||||
NtWaitForKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
|
||||
}
|
||||
}
|
||||
#else
|
||||
// Acquire writer lock
|
||||
imp_wait(0);
|
||||
|
||||
void shared_mutex::imp_lock_low(u32 val)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), val < c_err;
|
||||
// Convert to reader lock
|
||||
m_value += c_one - c_min;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
// Disabled code
|
||||
while (false)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
if (try_lock_low())
|
||||
const s64 value0 = m_value.fetch_op([](s64& value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire writer lock and downgrade
|
||||
const u32 old = m_value.fetch_add(c_one);
|
||||
|
||||
if (old == 0)
|
||||
{
|
||||
lock_downgrade();
|
||||
return;
|
||||
}
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
|
||||
imp_wait();
|
||||
lock_downgrade();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock_low(u32 old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), old - 1 < c_err;
|
||||
|
||||
// Check reader count, notify the writer if necessary
|
||||
if ((old - 1) % c_vip == 0)
|
||||
{
|
||||
imp_signal();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock_vip(u32 val)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), val < c_err;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
if (try_lock_vip())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire writer lock and downgrade
|
||||
const u32 old = m_value.fetch_add(c_one);
|
||||
|
||||
if (old == 0)
|
||||
{
|
||||
lock_downgrade_to_vip();
|
||||
return;
|
||||
}
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
|
||||
imp_wait();
|
||||
lock_downgrade_to_vip();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock_vip(u32 old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), old - 1 < c_err;
|
||||
|
||||
// Check reader count, notify the writer if necessary
|
||||
if ((old - 1) % c_one / c_vip == 0)
|
||||
{
|
||||
imp_signal();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::imp_wait()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
const auto [old, ok] = m_value.fetch_op([](u32& value)
|
||||
{
|
||||
if (value >= c_sig)
|
||||
if (value >= c_min)
|
||||
{
|
||||
value -= c_sig;
|
||||
return true;
|
||||
value -= c_min;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (ok)
|
||||
if (value0 >= c_min)
|
||||
{
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
m_value.wait(old);
|
||||
// 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_BITSET_PRIVATE, int(value1 >> 32), nullptr, nullptr, INT_MIN))
|
||||
{
|
||||
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_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, u32(c_sig >> 32));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::imp_signal()
|
||||
void shared_mutex::imp_wait(s64)
|
||||
{
|
||||
m_value += c_sig;
|
||||
m_value.notify_one();
|
||||
#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_LE_MACHINE, FUTEX_WAIT_BITSET_PRIVATE, int(value >> 32), nullptr, nullptr, u32(c_sig >> 32));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock(u32 val)
|
||||
void shared_mutex::imp_lock(s64 _old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), val < c_err;
|
||||
verify("shared_mutex overflow" HERE), _old <= c_max;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
if (!m_value && try_lock())
|
||||
const s64 value = m_value.load();
|
||||
|
||||
if (value == c_one && m_value.compare_and_swap_test(c_one, 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const u32 old = m_value.fetch_add(c_one);
|
||||
|
||||
if (old == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
|
||||
imp_wait();
|
||||
imp_wait(m_value.load());
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock(u32 old)
|
||||
void shared_mutex::imp_unlock(s64 _old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), old - c_one < c_err;
|
||||
verify("shared_mutex overflow" HERE), _old + c_one <= c_max;
|
||||
|
||||
// 1) Notify the next writer if necessary
|
||||
// 2) Notify all readers otherwise if necessary (currently indistinguishable from writers)
|
||||
if (old - c_one)
|
||||
// 2) Notify all readers otherwise if necessary
|
||||
#ifdef _WIN32
|
||||
if (_old + c_one <= 0)
|
||||
{
|
||||
imp_signal();
|
||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||
}
|
||||
else if (s64 count = -_old / c_min * 0)
|
||||
{
|
||||
// Disabled code
|
||||
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_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, u32(c_sig >> 32));
|
||||
}
|
||||
else if (false)
|
||||
{
|
||||
// Disabled code
|
||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, INT_MAX, nullptr, nullptr, INT_MIN);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock_upgrade()
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
busy_wait();
|
||||
|
||||
if (try_lock_upgrade())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to writer lock
|
||||
const u32 old = m_value.fetch_add(c_one - 1);
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one - 1 < c_sig;
|
||||
|
||||
if (old % c_one == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
imp_wait();
|
||||
unlock_shared();
|
||||
lock();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock_unlock()
|
||||
void shared_mutex::imp_lock_degrade()
|
||||
{
|
||||
u32 _max = 1;
|
||||
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
const u32 val = m_value;
|
||||
|
||||
if (val % c_one == 0 && (val / c_one < _max || val >= c_sig))
|
||||
{
|
||||
// Return if have cought a state where:
|
||||
// 1) Mutex is free
|
||||
// 2) Total number of waiters decreased since last check
|
||||
// 3) Signal bit is set (if used on the platform)
|
||||
return;
|
||||
}
|
||||
|
||||
_max = val / c_one;
|
||||
|
||||
busy_wait(1500);
|
||||
}
|
||||
|
||||
// Lock and unlock
|
||||
if (!m_value.fetch_add(c_one))
|
||||
{
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
imp_wait();
|
||||
unlock();
|
||||
lock_shared();
|
||||
}
|
||||
|
||||
bool shared_mutex::downgrade_unique_vip_lock_to_low_or_unlock()
|
||||
bool shared_mutex::try_lock_shared()
|
||||
{
|
||||
return m_value.atomic_op([](u32& value)
|
||||
{
|
||||
if (value % c_one / c_vip == 1)
|
||||
{
|
||||
value -= c_vip - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
value -= c_vip;
|
||||
return false;
|
||||
});
|
||||
// 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);
|
||||
}
|
||||
|
||||
+63
-219
@@ -1,171 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "types.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
|
||||
// Shared mutex with small size (u32).
|
||||
// Shared mutex.
|
||||
class shared_mutex final
|
||||
{
|
||||
enum : u32
|
||||
enum : s64
|
||||
{
|
||||
c_one = 1u << 14, // Fixed-point 1.0 value (one writer, max_readers = c_one - 1)
|
||||
c_sig = 1u << 30,
|
||||
c_err = 1u << 31,
|
||||
c_vip = 1u << 7,
|
||||
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<u32> m_value{};
|
||||
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_shared(u32 val);
|
||||
void imp_unlock_shared(u32 old);
|
||||
void imp_lock_low(u32 val);
|
||||
void imp_unlock_low(u32 old);
|
||||
void imp_lock_vip(u32 val);
|
||||
void imp_unlock_vip(u32 old);
|
||||
void imp_wait();
|
||||
void imp_signal();
|
||||
void imp_lock(u32 val);
|
||||
void imp_unlock(u32 old);
|
||||
void imp_lock_upgrade();
|
||||
void imp_lock_unlock();
|
||||
void imp_lock_degrade();
|
||||
|
||||
public:
|
||||
constexpr shared_mutex() = default;
|
||||
|
||||
bool try_lock_shared()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
// Conditional increment
|
||||
return value < c_one - 1 && m_value.compare_and_swap_test(value, value + 1);
|
||||
}
|
||||
bool try_lock_shared();
|
||||
|
||||
void lock_shared()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
const s64 value = m_value.load();
|
||||
|
||||
if (UNLIKELY(value >= c_one - 1 || !m_value.compare_and_swap_test(value, value + 1)))
|
||||
// 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 lock_shared_hle()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
if (LIKELY(value < c_one - 1))
|
||||
{
|
||||
u32 old = value;
|
||||
if (LIKELY(atomic_storage<u32>::compare_exchange_hle_acq(m_value.raw(), old, value + 1)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
imp_lock_shared(value);
|
||||
}
|
||||
|
||||
void unlock_shared()
|
||||
{
|
||||
// Unconditional decrement (can result in broken state)
|
||||
const u32 value = m_value.fetch_sub(1);
|
||||
// Unconditional increment
|
||||
const s64 value = m_value.fetch_add(c_min);
|
||||
|
||||
if (UNLIKELY(value >= c_one))
|
||||
if (value < 0 || value > c_one - c_min)
|
||||
{
|
||||
imp_unlock_shared(value);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_shared_hle()
|
||||
{
|
||||
const u32 value = atomic_storage<u32>::fetch_add_hle_rel(m_value.raw(), -1);
|
||||
|
||||
if (UNLIKELY(value >= c_one))
|
||||
{
|
||||
imp_unlock_shared(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_low()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
// Conditional increment
|
||||
return value < c_vip - 1 && m_value.compare_and_swap_test(value, value + 1);
|
||||
}
|
||||
|
||||
void lock_low()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
if (UNLIKELY(value >= c_vip - 1 || !m_value.compare_and_swap_test(value, value + 1)))
|
||||
{
|
||||
imp_lock_low(value);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_low()
|
||||
{
|
||||
// Unconditional decrement (can result in broken state)
|
||||
const u32 value = m_value.fetch_sub(1);
|
||||
|
||||
if (UNLIKELY(value >= c_one))
|
||||
{
|
||||
imp_unlock_low(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_vip()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
// Conditional increment
|
||||
return value < c_one - 1 && (value % c_vip) == 0 && m_value.compare_and_swap_test(value, value + c_vip);
|
||||
}
|
||||
|
||||
void lock_vip()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
if (UNLIKELY(value >= c_one - 1 || (value % c_vip) || !m_value.compare_and_swap_test(value, value + c_vip)))
|
||||
{
|
||||
imp_lock_vip(value);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_vip()
|
||||
{
|
||||
// Unconditional decrement (can result in broken state)
|
||||
const u32 value = m_value.fetch_sub(c_vip);
|
||||
|
||||
if (UNLIKELY(value >= c_one))
|
||||
{
|
||||
imp_unlock_vip(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock()
|
||||
{
|
||||
return m_value.compare_and_swap_test(0, c_one);
|
||||
}
|
||||
bool try_lock();
|
||||
|
||||
void lock()
|
||||
{
|
||||
const u32 value = m_value.compare_and_swap(0, c_one);
|
||||
// Try to lock
|
||||
const s64 value = m_value.compare_and_swap(c_one, 0);
|
||||
|
||||
if (UNLIKELY(value))
|
||||
{
|
||||
imp_lock(value);
|
||||
}
|
||||
}
|
||||
|
||||
void lock_hle()
|
||||
{
|
||||
u32 value = 0;
|
||||
|
||||
if (UNLIKELY(!atomic_storage<u32>::compare_exchange_hle_acq(m_value.raw(), value, c_one)))
|
||||
if (value != c_one)
|
||||
{
|
||||
imp_lock(value);
|
||||
}
|
||||
@@ -173,96 +67,64 @@ public:
|
||||
|
||||
void unlock()
|
||||
{
|
||||
// Unconditional decrement (can result in broken state)
|
||||
const u32 value = m_value.fetch_sub(c_one);
|
||||
// Unconditional increment
|
||||
const s64 value = m_value.fetch_add(c_one);
|
||||
|
||||
if (UNLIKELY(value != c_one))
|
||||
if (value != 0)
|
||||
{
|
||||
imp_unlock(value);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_hle()
|
||||
{
|
||||
const u32 value = atomic_storage<u32>::fetch_add_hle_rel(m_value.raw(), 0u - c_one);
|
||||
|
||||
if (UNLIKELY(value != c_one))
|
||||
{
|
||||
imp_unlock(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool try_lock_upgrade()
|
||||
{
|
||||
const u32 value = m_value.load();
|
||||
|
||||
// Conditional increment, try to convert a single reader into a writer, ignoring other writers
|
||||
return (value + c_one - 1) % c_one == 0 && m_value.compare_and_swap_test(value, value + c_one - 1);
|
||||
}
|
||||
bool try_lock_upgrade();
|
||||
|
||||
void lock_upgrade()
|
||||
{
|
||||
if (UNLIKELY(!try_lock_upgrade()))
|
||||
if (!m_value.compare_and_swap_test(c_one - c_min, 0))
|
||||
{
|
||||
imp_lock_upgrade();
|
||||
}
|
||||
}
|
||||
|
||||
void lock_downgrade()
|
||||
{
|
||||
// Convert to reader lock (can result in broken state)
|
||||
m_value -= c_one - 1;
|
||||
}
|
||||
bool try_lock_degrade();
|
||||
|
||||
void lock_downgrade_to_vip()
|
||||
void lock_degrade()
|
||||
{
|
||||
// Convert to vip lock (can result in broken state)
|
||||
m_value -= c_one - c_vip;
|
||||
}
|
||||
|
||||
// Optimized wait for lockability without locking, relaxed
|
||||
void lock_unlock()
|
||||
{
|
||||
if (UNLIKELY(m_value != 0))
|
||||
if (!m_value.compare_and_swap_test(0, c_one - c_min))
|
||||
{
|
||||
imp_lock_unlock();
|
||||
imp_lock_degrade();
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether can immediately obtain an exclusive (writer) lock
|
||||
bool is_free() const
|
||||
{
|
||||
return m_value.load() == 0;
|
||||
}
|
||||
|
||||
// Check whether can immediately obtain a shared (reader) lock
|
||||
bool is_lockable() const
|
||||
{
|
||||
return m_value.load() < c_one - 1;
|
||||
}
|
||||
|
||||
// Special purpose logic
|
||||
bool downgrade_unique_vip_lock_to_low_or_unlock();
|
||||
};
|
||||
|
||||
// Simplified shared (reader) lock implementation. Mutually incompatible with low_lock and vip_lock.
|
||||
// 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;
|
||||
|
||||
reader_lock& operator=(const reader_lock&) = delete;
|
||||
|
||||
explicit reader_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
m_mutex.lock_shared();
|
||||
lock();
|
||||
}
|
||||
|
||||
// One-way lock upgrade; note that the observed state could have been changed
|
||||
// One-way lock upgrade
|
||||
void upgrade()
|
||||
{
|
||||
if (!m_upgraded)
|
||||
@@ -272,58 +134,40 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Try to upgrade; if it succeeds, the observed state has NOT been changed
|
||||
bool try_upgrade()
|
||||
{
|
||||
return m_upgraded || (m_upgraded = m_mutex.try_lock_upgrade());
|
||||
}
|
||||
|
||||
~reader_lock()
|
||||
{
|
||||
m_upgraded ? m_mutex.unlock() : m_mutex.unlock_shared();
|
||||
unlock();
|
||||
}
|
||||
};
|
||||
|
||||
// Special shared (reader) lock, mutually exclusive with vip locks. Mutually incompatible with normal shared (reader) lock.
|
||||
class low_lock final
|
||||
// Simplified exclusive (writer) lock implementation.
|
||||
class writer_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
|
||||
public:
|
||||
low_lock(const low_lock&) = delete;
|
||||
|
||||
low_lock& operator=(const low_lock&) = delete;
|
||||
|
||||
explicit low_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
void lock()
|
||||
{
|
||||
m_mutex.lock_low();
|
||||
m_mutex.lock();
|
||||
}
|
||||
|
||||
~low_lock()
|
||||
void unlock()
|
||||
{
|
||||
m_mutex.unlock_low();
|
||||
m_mutex.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
// Special shared (reader) lock, mutually exclusive with low locks. Mutually incompatible with normal shared (reader) lock.
|
||||
class vip_lock final
|
||||
{
|
||||
shared_mutex& m_mutex;
|
||||
friend class cond_variable;
|
||||
|
||||
public:
|
||||
vip_lock(const vip_lock&) = delete;
|
||||
writer_lock(const writer_lock&) = delete;
|
||||
|
||||
vip_lock& operator=(const vip_lock&) = delete;
|
||||
|
||||
explicit vip_lock(shared_mutex& mutex)
|
||||
explicit writer_lock(shared_mutex& mutex)
|
||||
: m_mutex(mutex)
|
||||
{
|
||||
m_mutex.lock_vip();
|
||||
lock();
|
||||
}
|
||||
|
||||
~vip_lock()
|
||||
~writer_lock()
|
||||
{
|
||||
m_mutex.unlock_vip();
|
||||
unlock();
|
||||
}
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+3
-2
@@ -61,9 +61,10 @@ rXmlDocument::rXmlDocument() : handle()
|
||||
{
|
||||
}
|
||||
|
||||
void rXmlDocument::Read(const std::string& data)
|
||||
void rXmlDocument::Load(const std::string & path)
|
||||
{
|
||||
handle.load_buffer(data.data(), data.size());
|
||||
// TODO: Unsure of use of c_str.
|
||||
handle.load_file(path.c_str());
|
||||
}
|
||||
|
||||
std::shared_ptr<rXmlNode> rXmlDocument::GetRoot()
|
||||
|
||||
+6
-2
@@ -1,6 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <pugixml.hpp>
|
||||
#ifndef PUGIXML_HEADER_ONLY
|
||||
#define PUGIXML_HEADER_ONLY 1
|
||||
#endif // !PUGIXML_HEADER_ONLY
|
||||
#include "pugixml.hpp"
|
||||
#undef PUGIXML_HEADER_ONLY
|
||||
|
||||
struct rXmlNode
|
||||
{
|
||||
@@ -20,7 +24,7 @@ struct rXmlDocument
|
||||
rXmlDocument();
|
||||
rXmlDocument(const rXmlDocument& other) = delete;
|
||||
rXmlDocument &operator=(const rXmlDocument& other) = delete;
|
||||
void Read(const std::string& data);
|
||||
void Load(const std::string & path);
|
||||
std::shared_ptr<rXmlNode> GetRoot();
|
||||
|
||||
pugi::xml_document handle;
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
#ifndef MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
||||
#define MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
||||
|
||||
// Based on variant/recursive_wrapper.hpp from boost.
|
||||
//
|
||||
// Original license:
|
||||
//
|
||||
// Copyright (c) 2002-2003
|
||||
// Eric Friedman, Itay Maman
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
//namespace mapbox {
|
||||
//namespace util {
|
||||
namespace std {
|
||||
|
||||
template <typename T>
|
||||
class recursive_wrapper
|
||||
{
|
||||
|
||||
T* p_;
|
||||
|
||||
void assign(T const& rhs)
|
||||
{
|
||||
this->get() = rhs;
|
||||
}
|
||||
|
||||
public:
|
||||
using type = T;
|
||||
|
||||
/**
|
||||
* Default constructor default initializes the internally stored value.
|
||||
* For POD types this means nothing is done and the storage is
|
||||
* uninitialized.
|
||||
*
|
||||
* @throws std::bad_alloc if there is insufficient memory for an object
|
||||
* of type T.
|
||||
* @throws any exception thrown by the default constructur of T.
|
||||
*/
|
||||
recursive_wrapper()
|
||||
: p_(new T){}
|
||||
|
||||
~recursive_wrapper() noexcept { delete p_; }
|
||||
|
||||
recursive_wrapper(recursive_wrapper const& operand)
|
||||
: p_(new T(operand.get())) {}
|
||||
|
||||
recursive_wrapper(T const& operand)
|
||||
: p_(new T(operand)) {}
|
||||
|
||||
recursive_wrapper(recursive_wrapper&& operand)
|
||||
: p_(new T(std::move(operand.get()))) {}
|
||||
|
||||
recursive_wrapper(T&& operand)
|
||||
: p_(new T(std::move(operand))) {}
|
||||
|
||||
inline recursive_wrapper& operator=(recursive_wrapper const& rhs)
|
||||
{
|
||||
assign(rhs.get());
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline recursive_wrapper& operator=(T const& rhs)
|
||||
{
|
||||
assign(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void swap(recursive_wrapper& operand) noexcept
|
||||
{
|
||||
T* temp = operand.p_;
|
||||
operand.p_ = p_;
|
||||
p_ = temp;
|
||||
}
|
||||
|
||||
recursive_wrapper& operator=(recursive_wrapper&& rhs) noexcept
|
||||
{
|
||||
swap(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
recursive_wrapper& operator=(T&& rhs)
|
||||
{
|
||||
get() = std::move(rhs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
T& get()
|
||||
{
|
||||
assert(p_);
|
||||
return *get_pointer();
|
||||
}
|
||||
|
||||
T const& get() const
|
||||
{
|
||||
assert(p_);
|
||||
return *get_pointer();
|
||||
}
|
||||
|
||||
T* get_pointer() { return p_; }
|
||||
|
||||
const T* get_pointer() const { return p_; }
|
||||
|
||||
operator T const&() const { return this->get(); }
|
||||
|
||||
operator T&() { return this->get(); }
|
||||
|
||||
}; // class recursive_wrapper
|
||||
|
||||
template <typename T>
|
||||
inline void swap(recursive_wrapper<T>& lhs, recursive_wrapper<T>& rhs) noexcept
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
}
|
||||
//} // namespace util
|
||||
//} // namespace mapbox
|
||||
|
||||
#endif // MAPBOX_UTIL_RECURSIVE_WRAPPER_HPP
|
||||
+32
-6
@@ -1,4 +1,5 @@
|
||||
#include "sema.h"
|
||||
#include "sync.h"
|
||||
|
||||
void semaphore_base::imp_wait()
|
||||
{
|
||||
@@ -14,10 +15,18 @@ void semaphore_base::imp_wait()
|
||||
}
|
||||
}
|
||||
|
||||
#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.atomic_op([](s32& value)
|
||||
const s32 value = m_value.op_fetch([](s32& value)
|
||||
{
|
||||
// Use sign bit to acknowledge waiter presence
|
||||
if (value && value > INT32_MIN)
|
||||
@@ -35,26 +44,43 @@ void semaphore_base::imp_wait()
|
||||
// Set sign bit
|
||||
value = INT32_MIN;
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
|
||||
if (value >= 0)
|
||||
{
|
||||
// Signal other waiter to wake up or to restore sign bit
|
||||
m_value.notify_one();
|
||||
futex(&m_value.raw(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
m_value.wait(value);
|
||||
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;
|
||||
|
||||
m_value.notify_one();
|
||||
#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)
|
||||
|
||||
+41
-12
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "types.h"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Atomic.h"
|
||||
|
||||
// Lightweight semaphore helper class
|
||||
class semaphore_base
|
||||
@@ -14,6 +13,8 @@ class semaphore_base
|
||||
|
||||
void imp_post(s32 _old);
|
||||
|
||||
friend class semaphore_lock;
|
||||
|
||||
protected:
|
||||
explicit constexpr semaphore_base(s32 value)
|
||||
: m_value{value}
|
||||
@@ -32,10 +33,7 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
bool try_wait()
|
||||
{
|
||||
return m_value.try_dec(0);
|
||||
}
|
||||
bool try_wait();
|
||||
|
||||
void post(s32 _max)
|
||||
{
|
||||
@@ -75,36 +73,36 @@ class semaphore final : public semaphore_base
|
||||
public:
|
||||
// Default constructor (recommended)
|
||||
constexpr semaphore()
|
||||
: base(Def)
|
||||
: base{Def}
|
||||
{
|
||||
}
|
||||
|
||||
// Explicit value constructor (not recommended)
|
||||
explicit constexpr semaphore(s32 value)
|
||||
: base(value)
|
||||
: base{value}
|
||||
{
|
||||
}
|
||||
|
||||
// Obtain a semaphore
|
||||
void lock()
|
||||
void wait()
|
||||
{
|
||||
return base::wait();
|
||||
}
|
||||
|
||||
// Try to obtain a semaphore
|
||||
bool try_lock()
|
||||
explicit_bool_t try_wait()
|
||||
{
|
||||
return base::try_wait();
|
||||
}
|
||||
|
||||
// Return a semaphore
|
||||
void unlock()
|
||||
void post()
|
||||
{
|
||||
return base::post(Max);
|
||||
}
|
||||
|
||||
// Try to return a semaphore
|
||||
bool try_unlock()
|
||||
explicit_bool_t try_post()
|
||||
{
|
||||
return base::try_post(Max);
|
||||
}
|
||||
@@ -115,3 +113,34 @@ public:
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user