Merge branch 'master' into windows-clang

This commit is contained in:
qurious-pixel 2025-11-14 15:30:06 -08:00 committed by GitHub
commit b66d94b398
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
88 changed files with 1972 additions and 798 deletions

View file

@ -9,9 +9,9 @@ export HOMEBREW_NO_INSTALL_CLEANUP=1
brew install -f --overwrite --quiet ccache pipenv googletest ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers brew install -f --overwrite --quiet ccache pipenv googletest ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers
brew link -f --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5 brew link -f --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5
# moltenvk based on commit for 1.3.0 release # moltenvk based on commit for 1.4.0 release
export HOMEBREW_DEVELOPER=1 # Prevents blocking of local formulae export HOMEBREW_DEVELOPER=1 # Prevents blocking of local formulae
wget https://raw.githubusercontent.com/Homebrew/homebrew-core/7255441cbcafabaa8950f67c7ec55ff499dbb2d3/Formula/m/molten-vk.rb wget https://raw.githubusercontent.com/Homebrew/homebrew-core/ea2bec5f1f4384e188d7fc0702ab21a20a2ced08/Formula/m/molten-vk.rb
/opt/homebrew/bin/brew install -f --overwrite --formula --quiet ./molten-vk.rb /opt/homebrew/bin/brew install -f --overwrite --formula --quiet ./molten-vk.rb
export HOMEBREW_DEVELOPER=0 export HOMEBREW_DEVELOPER=0

View file

@ -15,9 +15,9 @@ arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebr
arch -x86_64 /usr/local/bin/brew install -f --overwrite --quiet ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers arch -x86_64 /usr/local/bin/brew install -f --overwrite --quiet ffmpeg@5 "llvm@$LLVM_COMPILER_VER" glew sdl3 vulkan-headers
arch -x86_64 /usr/local/bin/brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5 arch -x86_64 /usr/local/bin/brew link -f --overwrite --quiet "llvm@$LLVM_COMPILER_VER" ffmpeg@5
# moltenvk based on commit for 1.3.0 release # moltenvk based on commit for 1.4.0 release
export HOMEBREW_DEVELOPER=1 # Prevents blocking of local formulae export HOMEBREW_DEVELOPER=1 # Prevents blocking of local formulae
wget https://raw.githubusercontent.com/Homebrew/homebrew-core/7255441cbcafabaa8950f67c7ec55ff499dbb2d3/Formula/m/molten-vk.rb wget https://raw.githubusercontent.com/Homebrew/homebrew-core/ea2bec5f1f4384e188d7fc0702ab21a20a2ced08/Formula/m/molten-vk.rb
arch -x86_64 /usr/local/bin/brew install -f --overwrite --formula --quiet ./molten-vk.rb arch -x86_64 /usr/local/bin/brew install -f --overwrite --formula --quiet ./molten-vk.rb
export HOMEBREW_DEVELOPER=0 export HOMEBREW_DEVELOPER=0
export CXX=clang++ export CXX=clang++

View file

@ -32,6 +32,25 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
# Remove git directory containing local commit history file # Remove git directory containing local commit history file
rm -rf ./AppDir/usr/share/rpcs3/git rm -rf ./AppDir/usr/share/rpcs3/git
# Download translations
mkdir -p "./AppDir/usr/translations"
ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \
| grep "browser_download_url" \
| grep "RPCS3-languages.zip" \
| cut -d '"' -f 4)
if [ -z "$ZIP_URL" ]; then
echo "Failed to find RPCS3-languages.zip in the latest release. Continuing without translations."
else
echo "Downloading translations from: $ZIP_URL"
curl -L -o translations.zip "$ZIP_URL" || {
echo "Failed to download translations.zip. Continuing without translations."
exit 0
}
unzip -o translations.zip -d "./AppDir/usr/translations" >/dev/null 2>&1 || \
echo "Failed to extract translations.zip. Continuing without translations."
rm -f translations.zip
fi
curl -fsSLo /uruntime "https://github.com/VHSgunzo/uruntime/releases/download/v0.3.4/uruntime-appimage-dwarfs-$CPU_ARCH" curl -fsSLo /uruntime "https://github.com/VHSgunzo/uruntime/releases/download/v0.3.4/uruntime-appimage-dwarfs-$CPU_ARCH"
chmod +x /uruntime chmod +x /uruntime
/uruntime --appimage-mkdwarfs -f --set-owner 0 --set-group 0 --no-history --no-create-timestamp \ /uruntime --appimage-mkdwarfs -f --set-owner 0 --set-group 0 --no-history --no-create-timestamp \

View file

@ -31,6 +31,25 @@ rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \
../../.ci/optimize-mac.sh rpcs3.app ../../.ci/optimize-mac.sh rpcs3.app
# Download translations
mkdir -p "rpcs3.app/Contents/translations"
ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \
| grep "browser_download_url" \
| grep "RPCS3-languages.zip" \
| cut -d '"' -f 4)
if [ -z "$ZIP_URL" ]; then
echo "Failed to find RPCS3-languages.zip in the latest release. Continuing without translations."
else
echo "Downloading translations from: $ZIP_URL"
curl -L -o translations.zip "$ZIP_URL" || {
echo "Failed to download translations.zip. Continuing without translations."
exit 0
}
unzip -o translations.zip -d "rpcs3.app/Contents/translations" >/dev/null 2>&1 || \
echo "Failed to extract translations.zip. Continuing without translations."
rm -f translations.zip
fi
# Hack # Hack
install_name_tool -delete_rpath /opt/homebrew/lib RPCS3.app/Contents/MacOS/rpcs3 || echo "Hack for deleting rpath /opt/homebrew/lib not needed" install_name_tool -delete_rpath /opt/homebrew/lib RPCS3.app/Contents/MacOS/rpcs3 || echo "Hack for deleting rpath /opt/homebrew/lib not needed"
install_name_tool -delete_rpath /opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/lib RPCS3.app/Contents/MacOS/rpcs3 || echo "Hack for deleting rpath /opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/lib not needed" install_name_tool -delete_rpath /opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/lib RPCS3.app/Contents/MacOS/rpcs3 || echo "Hack for deleting rpath /opt/homebrew/opt/llvm@$LLVM_COMPILER_VER/lib not needed"

View file

@ -32,6 +32,25 @@ rm -rf "rpcs3.app/Contents/Frameworks/QtPdf.framework" \
../../.ci/optimize-mac.sh rpcs3.app ../../.ci/optimize-mac.sh rpcs3.app
# Download translations
mkdir -p "rpcs3.app/Contents/translations"
ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \
| grep "browser_download_url" \
| grep "RPCS3-languages.zip" \
| cut -d '"' -f 4)
if [ -z "$ZIP_URL" ]; then
echo "Failed to find RPCS3-languages.zip in the latest release. Continuing without translations."
else
echo "Downloading translations from: $ZIP_URL"
curl -L -o translations.zip "$ZIP_URL" || {
echo "Failed to download translations.zip. Continuing without translations."
exit 0
}
unzip -o translations.zip -d "rpcs3.app/Contents/translations" >/dev/null 2>&1 || \
echo "Failed to extract translations.zip. Continuing without translations."
rm -f translations.zip
fi
# Need to do this rename hack due to case insensitive filesystem # Need to do this rename hack due to case insensitive filesystem
mv rpcs3.app RPCS3_.app mv rpcs3.app RPCS3_.app
mv RPCS3_.app RPCS3.app mv RPCS3_.app RPCS3.app

View file

@ -24,6 +24,25 @@ mkdir ./bin/config/input_configs
curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./bin/config/input_configs/gamecontrollerdb.txt curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./bin/config/input_configs/gamecontrollerdb.txt
curl -fsSL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -t UTF-8 1> ./bin/GuiConfigs/compat_database.dat curl -fsSL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -t UTF-8 1> ./bin/GuiConfigs/compat_database.dat
# Download translations
mkdir -p ./bin/share/qt6/translations
ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \
| grep "browser_download_url" \
| grep "RPCS3-languages.zip" \
| cut -d '"' -f 4)
if [ -z "$ZIP_URL" ]; then
echo "Failed to find RPCS3-languages.zip in the latest release. Continuing without translations."
else
echo "Downloading translations from: $ZIP_URL"
curl -L -o translations.zip "$ZIP_URL" || {
echo "Failed to download translations.zip. Continuing without translations."
exit 0
}
unzip -o translations.zip -d "./bin/share/qt6/translations" >/dev/null 2>&1 || \
echo "Failed to extract translations.zip. Continuing without translations."
rm -f translations.zip
fi
# Package artifacts # Package artifacts
7z a -m0=LZMA2 -mx9 "$BUILD" ./bin/* 7z a -m0=LZMA2 -mx9 "$BUILD" ./bin/*

View file

@ -15,6 +15,25 @@ mkdir ./bin/config/input_configs
curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./bin/config/input_configs/gamecontrollerdb.txt curl -fsSL 'https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt' 1> ./bin/config/input_configs/gamecontrollerdb.txt
curl -fsSL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -t UTF-8 1> ./bin/GuiConfigs/compat_database.dat curl -fsSL 'https://rpcs3.net/compatibility?api=v1&export' | iconv -t UTF-8 1> ./bin/GuiConfigs/compat_database.dat
# Download translations
mkdir -p ./bin/qt6/translations
ZIP_URL=$(curl -fsSL "https://api.github.com/repos/RPCS3/rpcs3_translations/releases/latest" \
| grep "browser_download_url" \
| grep "RPCS3-languages.zip" \
| cut -d '"' -f 4)
if [ -z "$ZIP_URL" ]; then
echo "Failed to find RPCS3-languages.zip in the latest release. Continuing without translations."
else
echo "Downloading translations from: $ZIP_URL"
curl -L -o translations.zip "$ZIP_URL" || {
echo "Failed to download translations.zip. Continuing without translations."
exit 0
}
unzip -o translations.zip -d "./bin/qt6/translations" >/dev/null 2>&1 || \
echo "Failed to extract translations.zip. Continuing without translations."
rm -f translations.zip
fi
# Download SSL certificate (not needed with CURLSSLOPT_NATIVE_CA) # Download SSL certificate (not needed with CURLSSLOPT_NATIVE_CA)
#curl -fsSL 'https://curl.haxx.se/ca/cacert.pem' 1> ./bin/cacert.pem #curl -fsSL 'https://curl.haxx.se/ca/cacert.pem' 1> ./bin/cacert.pem

2
3rdparty/FAudio vendored

@ -1 +1 @@
Subproject commit ba876ce3be73eabd7094fa276a751ede8328b608 Subproject commit 8de3616b5b204260fe639e76587731d8a73b8d2c

View file

@ -3,7 +3,7 @@ include(ExternalProject)
ExternalProject_Add(moltenvk ExternalProject_Add(moltenvk
GIT_REPOSITORY https://github.com/KhronosGroup/MoltenVK.git GIT_REPOSITORY https://github.com/KhronosGroup/MoltenVK.git
GIT_TAG 49b97f2 GIT_TAG 4588705
BUILD_IN_SOURCE 1 BUILD_IN_SOURCE 1
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK
CONFIGURE_COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/fetchDependencies" --macos CONFIGURE_COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK/fetchDependencies" --macos

2
3rdparty/curl/curl vendored

@ -1 +1 @@
Subproject commit 11b991232fbcaa88e2b1faecac224416b0001e35 Subproject commit 400fffa90f30c7a2dc762fa33009d24851bd2016

View file

@ -80,10 +80,12 @@
<ClCompile Include="curl\lib\cshutdn.c" /> <ClCompile Include="curl\lib\cshutdn.c" />
<ClCompile Include="curl\lib\curlx\base64.c" /> <ClCompile Include="curl\lib\curlx\base64.c" />
<ClCompile Include="curl\lib\curlx\dynbuf.c" /> <ClCompile Include="curl\lib\curlx\dynbuf.c" />
<ClCompile Include="curl\lib\curlx\fopen.c" />
<ClCompile Include="curl\lib\curlx\inet_ntop.c" /> <ClCompile Include="curl\lib\curlx\inet_ntop.c" />
<ClCompile Include="curl\lib\curlx\inet_pton.c" /> <ClCompile Include="curl\lib\curlx\inet_pton.c" />
<ClCompile Include="curl\lib\curlx\multibyte.c" /> <ClCompile Include="curl\lib\curlx\multibyte.c" />
<ClCompile Include="curl\lib\curlx\nonblock.c" /> <ClCompile Include="curl\lib\curlx\nonblock.c" />
<ClCompile Include="curl\lib\curlx\strerr.c" />
<ClCompile Include="curl\lib\curlx\strparse.c" /> <ClCompile Include="curl\lib\curlx\strparse.c" />
<ClCompile Include="curl\lib\curlx\timediff.c" /> <ClCompile Include="curl\lib\curlx\timediff.c" />
<ClCompile Include="curl\lib\curlx\timeval.c" /> <ClCompile Include="curl\lib\curlx\timeval.c" />
@ -92,9 +94,9 @@
<ClCompile Include="curl\lib\curlx\warnless.c" /> <ClCompile Include="curl\lib\curlx\warnless.c" />
<ClCompile Include="curl\lib\curlx\winapi.c" /> <ClCompile Include="curl\lib\curlx\winapi.c" />
<ClCompile Include="curl\lib\curl_addrinfo.c" /> <ClCompile Include="curl\lib\curl_addrinfo.c" />
<ClCompile Include="curl\lib\curl_des.c" />
<ClCompile Include="curl\lib\curl_endian.c" /> <ClCompile Include="curl\lib\curl_endian.c" />
<ClCompile Include="curl\lib\curl_fnmatch.c" /> <ClCompile Include="curl\lib\curl_fnmatch.c" />
<ClCompile Include="curl\lib\curl_fopen.c" />
<ClCompile Include="curl\lib\curl_gethostname.c" /> <ClCompile Include="curl\lib\curl_gethostname.c" />
<ClCompile Include="curl\lib\curl_get_line.c" /> <ClCompile Include="curl\lib\curl_get_line.c" />
<ClCompile Include="curl\lib\curl_gssapi.c" /> <ClCompile Include="curl\lib\curl_gssapi.c" />
@ -120,7 +122,6 @@
<ClCompile Include="curl\lib\fake_addrinfo.c" /> <ClCompile Include="curl\lib\fake_addrinfo.c" />
<ClCompile Include="curl\lib\file.c" /> <ClCompile Include="curl\lib\file.c" />
<ClCompile Include="curl\lib\fileinfo.c" /> <ClCompile Include="curl\lib\fileinfo.c" />
<ClCompile Include="curl\lib\fopen.c" />
<ClCompile Include="curl\lib\formdata.c" /> <ClCompile Include="curl\lib\formdata.c" />
<ClCompile Include="curl\lib\ftp.c" /> <ClCompile Include="curl\lib\ftp.c" />
<ClCompile Include="curl\lib\ftplistparser.c" /> <ClCompile Include="curl\lib\ftplistparser.c" />
@ -147,7 +148,6 @@
<ClCompile Include="curl\lib\idn.c" /> <ClCompile Include="curl\lib\idn.c" />
<ClCompile Include="curl\lib\if2ip.c" /> <ClCompile Include="curl\lib\if2ip.c" />
<ClCompile Include="curl\lib\imap.c" /> <ClCompile Include="curl\lib\imap.c" />
<ClCompile Include="curl\lib\krb5.c" />
<ClCompile Include="curl\lib\ldap.c" /> <ClCompile Include="curl\lib\ldap.c" />
<ClCompile Include="curl\lib\llist.c" /> <ClCompile Include="curl\lib\llist.c" />
<ClCompile Include="curl\lib\macos.c" /> <ClCompile Include="curl\lib\macos.c" />
@ -159,6 +159,7 @@
<ClCompile Include="curl\lib\mqtt.c" /> <ClCompile Include="curl\lib\mqtt.c" />
<ClCompile Include="curl\lib\multi.c" /> <ClCompile Include="curl\lib\multi.c" />
<ClCompile Include="curl\lib\multi_ev.c" /> <ClCompile Include="curl\lib\multi_ev.c" />
<ClCompile Include="curl\lib\multi_ntfy.c" />
<ClCompile Include="curl\lib\netrc.c" /> <ClCompile Include="curl\lib\netrc.c" />
<ClCompile Include="curl\lib\noproxy.c" /> <ClCompile Include="curl\lib\noproxy.c" />
<ClCompile Include="curl\lib\openldap.c" /> <ClCompile Include="curl\lib\openldap.c" />
@ -203,6 +204,7 @@
<ClCompile Include="curl\lib\version.c" /> <ClCompile Include="curl\lib\version.c" />
<ClCompile Include="curl\lib\vquic\curl_ngtcp2.c" /> <ClCompile Include="curl\lib\vquic\curl_ngtcp2.c" />
<ClCompile Include="curl\lib\vquic\curl_quiche.c" /> <ClCompile Include="curl\lib\vquic\curl_quiche.c" />
<ClCompile Include="curl\lib\vtls\apple.c" />
<ClCompile Include="curl\lib\vtls\cipher_suite.c" /> <ClCompile Include="curl\lib\vtls\cipher_suite.c" />
<ClCompile Include="curl\lib\vtls\hostcheck.c" /> <ClCompile Include="curl\lib\vtls\hostcheck.c" />
<ClCompile Include="curl\lib\vtls\rustls.c" /> <ClCompile Include="curl\lib\vtls\rustls.c" />
@ -225,7 +227,6 @@
<ClCompile Include="curl\lib\vssh\curl_path.c" /> <ClCompile Include="curl\lib\vssh\curl_path.c" />
<ClCompile Include="curl\lib\vssh\libssh.c" /> <ClCompile Include="curl\lib\vssh\libssh.c" />
<ClCompile Include="curl\lib\vssh\libssh2.c" /> <ClCompile Include="curl\lib\vssh\libssh2.c" />
<ClCompile Include="curl\lib\vssh\wolfssh.c" />
<ClCompile Include="curl\lib\vtls\gtls.c" /> <ClCompile Include="curl\lib\vtls\gtls.c" />
<ClCompile Include="curl\lib\vtls\keylog.c" /> <ClCompile Include="curl\lib\vtls\keylog.c" />
<ClCompile Include="curl\lib\vtls\mbedtls.c" /> <ClCompile Include="curl\lib\vtls\mbedtls.c" />
@ -274,10 +275,12 @@
<ClInclude Include="curl\lib\curlx\binmode.h" /> <ClInclude Include="curl\lib\curlx\binmode.h" />
<ClInclude Include="curl\lib\curlx\curlx.h" /> <ClInclude Include="curl\lib\curlx\curlx.h" />
<ClInclude Include="curl\lib\curlx\dynbuf.h" /> <ClInclude Include="curl\lib\curlx\dynbuf.h" />
<ClInclude Include="curl\lib\curlx\fopen.h" />
<ClInclude Include="curl\lib\curlx\inet_ntop.h" /> <ClInclude Include="curl\lib\curlx\inet_ntop.h" />
<ClInclude Include="curl\lib\curlx\inet_pton.h" /> <ClInclude Include="curl\lib\curlx\inet_pton.h" />
<ClInclude Include="curl\lib\curlx\multibyte.h" /> <ClInclude Include="curl\lib\curlx\multibyte.h" />
<ClInclude Include="curl\lib\curlx\nonblock.h" /> <ClInclude Include="curl\lib\curlx\nonblock.h" />
<ClInclude Include="curl\lib\curlx\strerr.h" />
<ClInclude Include="curl\lib\curlx\strparse.h" /> <ClInclude Include="curl\lib\curlx\strparse.h" />
<ClInclude Include="curl\lib\curlx\timediff.h" /> <ClInclude Include="curl\lib\curlx\timediff.h" />
<ClInclude Include="curl\lib\curlx\timeval.h" /> <ClInclude Include="curl\lib\curlx\timeval.h" />
@ -287,14 +290,13 @@
<ClInclude Include="curl\lib\curlx\winapi.h" /> <ClInclude Include="curl\lib\curlx\winapi.h" />
<ClInclude Include="curl\lib\curl_addrinfo.h" /> <ClInclude Include="curl\lib\curl_addrinfo.h" />
<ClInclude Include="curl\lib\curl_ctype.h" /> <ClInclude Include="curl\lib\curl_ctype.h" />
<ClInclude Include="curl\lib\curl_des.h" />
<ClInclude Include="curl\lib\curl_endian.h" /> <ClInclude Include="curl\lib\curl_endian.h" />
<ClInclude Include="curl\lib\curl_fnmatch.h" /> <ClInclude Include="curl\lib\curl_fnmatch.h" />
<ClInclude Include="curl\lib\curl_fopen.h" />
<ClInclude Include="curl\lib\curl_gethostname.h" /> <ClInclude Include="curl\lib\curl_gethostname.h" />
<ClInclude Include="curl\lib\curl_get_line.h" /> <ClInclude Include="curl\lib\curl_get_line.h" />
<ClInclude Include="curl\lib\curl_gssapi.h" /> <ClInclude Include="curl\lib\curl_gssapi.h" />
<ClInclude Include="curl\lib\curl_hmac.h" /> <ClInclude Include="curl\lib\curl_hmac.h" />
<ClInclude Include="curl\lib\curl_krb5.h" />
<ClInclude Include="curl\lib\curl_ldap.h" /> <ClInclude Include="curl\lib\curl_ldap.h" />
<ClInclude Include="curl\lib\curl_md4.h" /> <ClInclude Include="curl\lib\curl_md4.h" />
<ClInclude Include="curl\lib\curl_md5.h" /> <ClInclude Include="curl\lib\curl_md5.h" />
@ -325,7 +327,6 @@
<ClInclude Include="curl\lib\fake_addrinfo.h" /> <ClInclude Include="curl\lib\fake_addrinfo.h" />
<ClInclude Include="curl\lib\file.h" /> <ClInclude Include="curl\lib\file.h" />
<ClInclude Include="curl\lib\fileinfo.h" /> <ClInclude Include="curl\lib\fileinfo.h" />
<ClInclude Include="curl\lib\fopen.h" />
<ClInclude Include="curl\lib\formdata.h" /> <ClInclude Include="curl\lib\formdata.h" />
<ClInclude Include="curl\lib\ftp.h" /> <ClInclude Include="curl\lib\ftp.h" />
<ClInclude Include="curl\lib\ftplistparser.h" /> <ClInclude Include="curl\lib\ftplistparser.h" />
@ -357,6 +358,7 @@
<ClInclude Include="curl\lib\multihandle.h" /> <ClInclude Include="curl\lib\multihandle.h" />
<ClInclude Include="curl\lib\multiif.h" /> <ClInclude Include="curl\lib\multiif.h" />
<ClInclude Include="curl\lib\multi_ev.h" /> <ClInclude Include="curl\lib\multi_ev.h" />
<ClInclude Include="curl\lib\multi_ntfy.h" />
<ClInclude Include="curl\lib\netrc.h" /> <ClInclude Include="curl\lib\netrc.h" />
<ClInclude Include="curl\lib\noproxy.h" /> <ClInclude Include="curl\lib\noproxy.h" />
<ClInclude Include="curl\lib\parsedate.h" /> <ClInclude Include="curl\lib\parsedate.h" />
@ -403,6 +405,7 @@
<ClInclude Include="curl\lib\vquic\curl_ngtcp2.h" /> <ClInclude Include="curl\lib\vquic\curl_ngtcp2.h" />
<ClInclude Include="curl\lib\vquic\curl_quiche.h" /> <ClInclude Include="curl\lib\vquic\curl_quiche.h" />
<ClInclude Include="curl\lib\vquic\vquic_int.h" /> <ClInclude Include="curl\lib\vquic\vquic_int.h" />
<ClInclude Include="curl\lib\vtls\apple.h" />
<ClInclude Include="curl\lib\vtls\cipher_suite.h" /> <ClInclude Include="curl\lib\vtls\cipher_suite.h" />
<ClInclude Include="curl\lib\vtls\hostcheck.h" /> <ClInclude Include="curl\lib\vtls\hostcheck.h" />
<ClInclude Include="curl\lib\vtls\rustls.h" /> <ClInclude Include="curl\lib\vtls\rustls.h" />

View file

@ -42,9 +42,6 @@
<ClCompile Include="curl\lib\curl_addrinfo.c"> <ClCompile Include="curl\lib\curl_addrinfo.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="curl\lib\curl_des.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\curl_endian.c"> <ClCompile Include="curl\lib\curl_endian.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -159,9 +156,6 @@
<ClCompile Include="curl\lib\imap.c"> <ClCompile Include="curl\lib\imap.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="curl\lib\krb5.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\ldap.c"> <ClCompile Include="curl\lib\ldap.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -333,9 +327,6 @@
<ClCompile Include="curl\lib\vssh\libssh2.c"> <ClCompile Include="curl\lib\vssh\libssh2.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="curl\lib\vssh\wolfssh.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\vtls\gtls.c"> <ClCompile Include="curl\lib\vtls\gtls.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -396,9 +387,6 @@
<ClCompile Include="curl\lib\headers.c"> <ClCompile Include="curl\lib\headers.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="curl\lib\fopen.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\noproxy.c"> <ClCompile Include="curl\lib\noproxy.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -546,6 +534,21 @@
<ClCompile Include="curl\lib\cf-ip-happy.c"> <ClCompile Include="curl\lib\cf-ip-happy.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="curl\lib\curl_fopen.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\multi_ntfy.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\vtls\apple.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\curlx\fopen.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="curl\lib\curlx\strerr.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="curl\include\curl\curl.h"> <ClInclude Include="curl\include\curl\curl.h">
@ -623,9 +626,6 @@
<ClInclude Include="curl\lib\curl_ctype.h"> <ClInclude Include="curl\lib\curl_ctype.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="curl\lib\curl_des.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curl_endian.h"> <ClInclude Include="curl\lib\curl_endian.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -926,9 +926,6 @@
<ClInclude Include="curl\lib\vtls\keylog.h"> <ClInclude Include="curl\lib\vtls\keylog.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="curl\lib\curl_krb5.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\easyoptions.h"> <ClInclude Include="curl\lib\easyoptions.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -953,9 +950,6 @@
<ClInclude Include="curl\lib\easy_lock.h"> <ClInclude Include="curl\lib\easy_lock.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="curl\lib\fopen.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\functypes.h"> <ClInclude Include="curl\lib\functypes.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -1112,6 +1106,21 @@
<ClInclude Include="curl\lib\curl_mem_undef.h"> <ClInclude Include="curl\lib\curl_mem_undef.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="curl\lib\curl_fopen.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\multi_ntfy.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\vtls\apple.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curlx\fopen.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="curl\lib\curlx\strerr.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="curl\lib\libcurl.rc"> <ResourceCompile Include="curl\lib\libcurl.rc">

@ -1 +1 @@
Subproject commit a8589a84226a6202831a3d49ff4edda4acab9acd Subproject commit badbf8da4ee72b3ef599c721ffc9899e8d7c8d90

View file

@ -384,7 +384,6 @@
<ClCompile Include="SDL\src\render\opengl\SDL_shaders_gl.c" /> <ClCompile Include="SDL\src\render\opengl\SDL_shaders_gl.c" />
<ClCompile Include="SDL\src\render\opengles2\SDL_render_gles2.c" /> <ClCompile Include="SDL\src\render\opengles2\SDL_render_gles2.c" />
<ClCompile Include="SDL\src\render\opengles2\SDL_shaders_gles2.c" /> <ClCompile Include="SDL\src\render\opengles2\SDL_shaders_gles2.c" />
<ClCompile Include="SDL\src\render\SDL_d3dmath.c" />
<ClCompile Include="SDL\src\render\SDL_render.c" /> <ClCompile Include="SDL\src\render\SDL_render.c" />
<ClCompile Include="SDL\src\render\SDL_render_unsupported.c" /> <ClCompile Include="SDL\src\render\SDL_render_unsupported.c" />
<ClCompile Include="SDL\src\render\SDL_yuv_sw.c" /> <ClCompile Include="SDL\src\render\SDL_yuv_sw.c" />

View file

@ -1457,9 +1457,6 @@
<ClCompile Include="SDL\src\sensor\windows\SDL_windowssensor.c"> <ClCompile Include="SDL\src\sensor\windows\SDL_windowssensor.c">
<Filter>sensor\windows</Filter> <Filter>sensor\windows</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="SDL\src\render\SDL_d3dmath.c">
<Filter>render</Filter>
</ClCompile>
<ClCompile Include="SDL\src\render\SDL_render.c"> <ClCompile Include="SDL\src\render\SDL_render.c">
<Filter>render</Filter> <Filter>render</Filter>
</ClCompile> </ClCompile>

View file

@ -9,7 +9,8 @@ else()
option(ZSTD_BUILD_SHARED "BUILD SHARED LIBRARIES" OFF) option(ZSTD_BUILD_SHARED "BUILD SHARED LIBRARIES" OFF)
option(ZSTD_BUILD_STATIC "BUILD STATIC LIBRARIES" ON) option(ZSTD_BUILD_STATIC "BUILD STATIC LIBRARIES" ON)
option(ZSTD_BUILD_TESTS "BUILD TESTS" OFF) option(ZSTD_BUILD_TESTS "BUILD TESTS" OFF)
add_subdirectory(zstd/build/cmake EXLUDE_FROM_ALL)
add_subdirectory(zstd/build/cmake EXCLUDE_FROM_ALL)
add_library(3rdparty_zstd INTERFACE) add_library(3rdparty_zstd INTERFACE)
target_link_libraries(3rdparty_zstd INTERFACE libzstd_static) target_link_libraries(3rdparty_zstd INTERFACE libzstd_static)
endif() endif()

27
Utilities/deferred_op.hpp Normal file
View file

@ -0,0 +1,27 @@
#pragma once
// Generic deferred routine wrapper
// Use-case is similar to "defer" statement in other languages, just invokes a callback when the object goes out of scope
#include <functional>
namespace utils
{
template <typename F>
requires std::is_invocable_v<F>
class deferred_op
{
public:
deferred_op(F&& callback)
: m_callback(callback)
{}
~deferred_op()
{
m_callback();
}
private:
F m_callback;
};
}

View file

@ -66,7 +66,7 @@ else ()
find_package_handle_standard_args(FFMPEG find_package_handle_standard_args(FFMPEG
DEFAULT_MSG DEFAULT_MSG
FFMPEG_LIBAVCODEC FFMPEG_LIBAVFORMAT FFMPEG_LIBSWSCALE FFMPEG_LIBSWRESAMPLE FFMPEG_LIBAVCODEC FFMPEG_LIBAVFORMAT FFMPEG_LIBAVUTIL FFMPEG_LIBSWSCALE FFMPEG_LIBSWRESAMPLE
) )
if (FFMPEG_FOUND) if (FFMPEG_FOUND)

View file

@ -25,6 +25,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <charconv> #include <charconv>
#include <string_view>
std::string to_hex(std::uint64_t value, bool prfx = true) std::string to_hex(std::uint64_t value, bool prfx = true)
{ {
@ -85,7 +86,7 @@ int main(int argc, char* argv[])
// Decode address and try to find the object // Decode address and try to find the object
std::uint64_t addr = -1; std::uint64_t addr = -1;
std::from_chars(arg.data() + strlen("--start-address=0x"), arg.data() + arg.size(), addr, 16); std::from_chars(arg.data() + ("--start-address=0x"sv).size(), arg.data() + arg.size(), addr, 16);
for (int j = 0; j < 0x100'0000; j++) for (int j = 0; j < 0x100'0000; j++)
{ {

View file

@ -10,16 +10,6 @@ include(CheckFunctionExists)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(ADDITIONAL_LIBS "")
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
#on some Linux distros shm_unlink and similar functions are in librt only
list(APPEND ADDITIONAL_LIBS "rt")
elseif(NOT WIN32 AND NOT CMAKE_CXX_FLAGS MATCHES "LIBICONV_PLUG")
#it seems like glibc includes the iconv functions we use but other libc
#implementations like the one on OSX don't seem implement them
list(APPEND ADDITIONAL_LIBS "iconv")
endif()
if(UNIX AND NOT APPLE AND NOT ANDROID) if(UNIX AND NOT APPLE AND NOT ANDROID)
add_compile_definitions(DATADIR="${CMAKE_INSTALL_FULL_DATADIR}/rpcs3") add_compile_definitions(DATADIR="${CMAKE_INSTALL_FULL_DATADIR}/rpcs3")
# Optionally enable X11 for window management # Optionally enable X11 for window management
@ -78,8 +68,16 @@ if (NOT ANDROID)
3rdparty::libcurl 3rdparty::libcurl
3rdparty::zlib 3rdparty::zlib
3rdparty::opencv 3rdparty::opencv
3rdparty::fusion 3rdparty::fusion)
${ADDITIONAL_LIBS})
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
#on some Linux distros shm_unlink and similar functions are in librt only
target_link_libraries(rpcs3_lib PRIVATE rt)
elseif(NOT WIN32 AND NOT CMAKE_CXX_FLAGS MATCHES "LIBICONV_PLUG")
#it seems like glibc includes the iconv functions we use but other libc
#implementations like the one on OSX don't seem implement them
target_link_libraries(rpcs3_lib PRIVATE iconv)
endif()
# Unix display manager # Unix display manager
if(X11_FOUND) if(X11_FOUND)
@ -106,19 +104,16 @@ if (NOT ANDROID)
endif() endif()
# Build rpcs3 executable # Build rpcs3 executable
add_executable(rpcs3 WIN32 MACOSX_BUNDLE)
if(WIN32) if(WIN32)
add_executable(rpcs3 WIN32)
target_sources(rpcs3 PRIVATE rpcs3.rc) target_sources(rpcs3 PRIVATE rpcs3.rc)
target_compile_definitions(rpcs3 PRIVATE UNICODE _UNICODE) target_compile_definitions(rpcs3 PRIVATE UNICODE _UNICODE)
elseif(APPLE) elseif(APPLE)
add_executable(rpcs3 MACOSX_BUNDLE)
target_sources(rpcs3 PRIVATE rpcs3.icns update_helper.sh) target_sources(rpcs3 PRIVATE rpcs3.icns update_helper.sh)
set_source_files_properties(update_helper.sh PROPERTIES MACOSX_PACKAGE_LOCATION Resources) set_source_files_properties(update_helper.sh PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
set_target_properties(rpcs3 set_target_properties(rpcs3
PROPERTIES PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.plist.in") MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.plist.in")
else()
add_executable(rpcs3)
endif() endif()
target_sources(rpcs3 target_sources(rpcs3
@ -137,17 +132,12 @@ if (NOT ANDROID)
# Copy icons to executable directory # Copy icons to executable directory
if(APPLE) if(APPLE)
if (CMAKE_BUILD_TYPE MATCHES "Debug" OR CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo")
set(QT_DEPLOY_FLAGS "-no-strip")
else()
set(QT_DEPLOY_FLAGS "")
endif()
qt_finalize_target(rpcs3) qt_finalize_target(rpcs3)
add_custom_command(TARGET rpcs3 POST_BUILD add_custom_command(TARGET rpcs3 POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.icns $<TARGET_FILE_DIR:rpcs3>/../Resources/rpcs3.icns COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/rpcs3.icns $<TARGET_FILE_DIR:rpcs3>/../Resources/rpcs3.icns
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/Icons $<TARGET_FILE_DIR:rpcs3>/../Resources/Icons COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/Icons $<TARGET_FILE_DIR:rpcs3>/../Resources/Icons
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3>/../Resources/GuiConfigs COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3>/../Resources/GuiConfigs
COMMAND "${MACDEPLOYQT_EXECUTABLE}" "${PROJECT_BINARY_DIR}/bin/rpcs3.app" "${QT_DEPLOY_FLAGS}") COMMAND "${MACDEPLOYQT_EXECUTABLE}" "${PROJECT_BINARY_DIR}/bin/rpcs3.app" "$<$<CONFIG:Debug,RelWithDebInfo>:-no-strip>")
elseif(UNIX) elseif(UNIX)
add_custom_command(TARGET rpcs3 POST_BUILD add_custom_command(TARGET rpcs3 POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/Icons $<TARGET_FILE_DIR:rpcs3>/Icons COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/bin/Icons $<TARGET_FILE_DIR:rpcs3>/Icons

View file

@ -403,6 +403,7 @@ target_sources(rpcs3_emu PRIVATE
Io/GunCon3.cpp Io/GunCon3.cpp
Io/Infinity.cpp Io/Infinity.cpp
Io/interception.cpp Io/interception.cpp
Io/KamenRider.cpp
Io/KeyboardHandler.cpp Io/KeyboardHandler.cpp
Io/midi_config_types.cpp Io/midi_config_types.cpp
Io/mouse_config.cpp Io/mouse_config.cpp

View file

@ -2284,6 +2284,8 @@ error_code cellGemClearStatusFlags(u32 gem_num, u64 mask)
error_code cellGemConvertVideoFinish(ppu_thread& ppu) error_code cellGemConvertVideoFinish(ppu_thread& ppu)
{ {
ppu.state += cpu_flag::wait;
cellGem.warning("cellGemConvertVideoFinish()"); cellGem.warning("cellGemConvertVideoFinish()");
auto& gem = g_fxo->get<gem_config>(); auto& gem = g_fxo->get<gem_config>();

View file

@ -1115,7 +1115,24 @@ void PPUTranslator::VCFSX(ppu_opcode_t op)
void PPUTranslator::VCFUX(ppu_opcode_t op) void PPUTranslator::VCFUX(ppu_opcode_t op)
{ {
const auto b = get_vr<u32[4]>(op.vb); const auto b = get_vr<u32[4]>(op.vb);
set_vr(op.vd, fpcast<f32[4]>(b) * fsplat<f32[4]>(std::pow(2, -static_cast<int>(op.vuimm))));
#ifdef ARCH_ARM64
return set_vr(op.vd, fpcast<f32[4]>(b) * fsplat<f32[4]>(std::pow(2, -static_cast<int>(op.vuimm))));
#else
if (m_use_avx512)
{
return set_vr(op.vd, fpcast<f32[4]>(b) * fsplat<f32[4]>(std::pow(2, -static_cast<int>(op.vuimm))));
}
constexpr int bit_shift = 9;
const auto shifted = (b >> bit_shift);
const auto cleared = shifted << bit_shift;
const auto low_bits = b - cleared;
const auto high_part = fpcast<f32[4]>(noncast<s32[4]>(shifted)) * fsplat<f32[4]>(static_cast<f32>(1u << bit_shift));
const auto low_part = fpcast<f32[4]>(noncast<s32[4]>(low_bits));
const auto temp = high_part + low_part;
set_vr(op.vd, temp * fsplat<f32[4]>(std::pow(2, -static_cast<int>(op.vuimm))));
#endif
} }
void PPUTranslator::VCMPBFP(ppu_opcode_t op) void PPUTranslator::VCMPBFP(ppu_opcode_t op)

View file

@ -3844,7 +3844,8 @@ bool spu_thread::do_putllc(const spu_mfc_cmd& args)
} }
// Writeback of unchanged data. Only check memory change // Writeback of unchanged data. Only check memory change
if (cmp_rdata(rdata, vm::_ref<spu_rdata_t>(addr)) && res.compare_and_swap_test(rtime, rtime + 128)) // For the comparison, load twice for atomicity
if (cmp_rdata(rdata, vm::_ref<spu_rdata_t>(addr)) && res == rtime && cmp_rdata(rdata, vm::_ref<spu_rdata_t>(addr)) && res.compare_and_swap_test(rtime, rtime + 128))
{ {
raddr = 0; // Disable notification raddr = 0; // Disable notification
return true; return true;

View file

@ -213,6 +213,8 @@ error_code sys_memory_allocate_from_container(cpu_thread& cpu, u64 size, u32 cid
if (alloc_addr) if (alloc_addr)
{ {
sys_memory.notice("sys_memory_allocate_from_container(): Allocated 0x%x address (size=0x%x)", addr, size);
vm::lock_sudo(addr, static_cast<u32>(size)); vm::lock_sudo(addr, static_cast<u32>(size));
cpu.check_state(); cpu.check_state();
*alloc_addr = addr; *alloc_addr = addr;

View file

@ -18,6 +18,7 @@
#include "Emu/Io/Skylander.h" #include "Emu/Io/Skylander.h"
#include "Emu/Io/Infinity.h" #include "Emu/Io/Infinity.h"
#include "Emu/Io/Dimensions.h" #include "Emu/Io/Dimensions.h"
#include "Emu/Io/KamenRider.h"
#include "Emu/Io/GHLtar.h" #include "Emu/Io/GHLtar.h"
#include "Emu/Io/ghltar_config.h" #include "Emu/Io/ghltar_config.h"
#include "Emu/Io/guncon3_config.h" #include "Emu/Io/guncon3_config.h"
@ -175,7 +176,7 @@ private:
{0x1430, 0x0150, 0x0150, "Skylanders Portal", &usb_device_skylander::get_num_emu_devices, &usb_device_skylander::make_instance}, {0x1430, 0x0150, 0x0150, "Skylanders Portal", &usb_device_skylander::get_num_emu_devices, &usb_device_skylander::make_instance},
{0x0E6F, 0x0129, 0x0129, "Disney Infinity Base", &usb_device_infinity::get_num_emu_devices, &usb_device_infinity::make_instance}, {0x0E6F, 0x0129, 0x0129, "Disney Infinity Base", &usb_device_infinity::get_num_emu_devices, &usb_device_infinity::make_instance},
{0x0E6F, 0x0241, 0x0241, "Lego Dimensions Portal", &usb_device_dimensions::get_num_emu_devices, &usb_device_dimensions::make_instance}, {0x0E6F, 0x0241, 0x0241, "Lego Dimensions Portal", &usb_device_dimensions::get_num_emu_devices, &usb_device_dimensions::make_instance},
{0x0E6F, 0x200A, 0x200A, "Kamen Rider Summonride Portal", nullptr, nullptr}, {0x0E6F, 0x200A, 0x200A, "Kamen Rider Summonride Portal", &usb_device_kamen_rider::get_num_emu_devices, &usb_device_kamen_rider::make_instance},
// Cameras // Cameras
// {0x1415, 0x0020, 0x2000, "Sony Playstation Eye", nullptr, nullptr}, // TODO: verifiy // {0x1415, 0x0020, 0x2000, "Sony Playstation Eye", nullptr, nullptr}, // TODO: verifiy

View file

@ -544,9 +544,7 @@ std::optional<std::array<u8, 32>> dimensions_toypad::pop_added_removed_response(
std::lock_guard lock(m_dimensions_mutex); std::lock_guard lock(m_dimensions_mutex);
if (m_figure_added_removed_responses.empty()) if (m_figure_added_removed_responses.empty())
{
return std::nullopt; return std::nullopt;
}
std::array<u8, 32> response = m_figure_added_removed_responses.front(); std::array<u8, 32> response = m_figure_added_removed_responses.front();
m_figure_added_removed_responses.pop(); m_figure_added_removed_responses.pop();
@ -597,7 +595,6 @@ void usb_device_dimensions::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoi
{ {
// Read Endpoint, if a request has not been sent via the write endpoint, set expected result as // Read Endpoint, if a request has not been sent via the write endpoint, set expected result as
// EHCI_CC_HALTED so the game doesn't report the Toypad as being disconnected. // EHCI_CC_HALTED so the game doesn't report the Toypad as being disconnected.
std::lock_guard lock(m_query_mutex);
std::optional<std::array<u8, 32>> response = g_dimensionstoypad.pop_added_removed_response(); std::optional<std::array<u8, 32>> response = g_dimensionstoypad.pop_added_removed_response();
if (response) if (response)
{ {
@ -696,7 +693,6 @@ void usb_device_dimensions::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoi
break; break;
} }
} }
std::lock_guard lock(m_query_mutex);
m_queries.push(q_result); m_queries.push(q_result);
break; break;
} }

View file

@ -79,6 +79,5 @@ public:
void isochronous_transfer(UsbTransfer* transfer) override; void isochronous_transfer(UsbTransfer* transfer) override;
protected: protected:
shared_mutex m_query_mutex;
std::queue<std::array<u8, 32>> m_queries; std::queue<std::array<u8, 32>> m_queries;
}; };

View file

@ -258,13 +258,13 @@ void infinity_base::get_figure_identifier(u8 fig_num, u8 sequence, std::array<u8
reply_buf[11] = generate_checksum(reply_buf, 11); reply_buf[11] = generate_checksum(reply_buf, 11);
} }
bool infinity_base::has_figure_been_added_removed() const std::optional<std::array<u8, 32>> infinity_base::pop_added_removed_response()
{ {
return !m_figure_added_removed_responses.empty(); std::lock_guard lock(infinity_mutex);
}
if (m_figure_added_removed_responses.empty())
return std::nullopt;
std::array<u8, 32> infinity_base::pop_added_removed_response()
{
std::array<u8, 32> response = m_figure_added_removed_responses.front(); std::array<u8, 32> response = m_figure_added_removed_responses.front();
m_figure_added_removed_responses.pop(); m_figure_added_removed_responses.pop();
return response; return response;
@ -399,9 +399,10 @@ void usb_device_infinity::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint
{ {
// Respond after FF command // Respond after FF command
transfer->expected_time = get_timestamp() + 1000; transfer->expected_time = get_timestamp() + 1000;
if (g_infinitybase.has_figure_been_added_removed()) std::optional<std::array<u8, 32>> response = g_infinitybase.pop_added_removed_response();
if (response)
{ {
memcpy(buf, g_infinitybase.pop_added_removed_response().data(), 0x20); memcpy(buf, response.value().data(), 0x20);
} }
else if (!m_queries.empty()) else if (!m_queries.empty())
{ {

View file

@ -3,6 +3,7 @@
#include "Emu/Io/usb_device.h" #include "Emu/Io/usb_device.h"
#include "Utilities/mutex.h" #include "Utilities/mutex.h"
#include <array> #include <array>
#include <optional>
#include <queue> #include <queue>
struct infinity_figure struct infinity_figure
@ -24,8 +25,7 @@ public:
void query_block(u8 fig_num, u8 block, std::array<u8, 32>& reply_buf, u8 sequence); void query_block(u8 fig_num, u8 block, std::array<u8, 32>& reply_buf, u8 sequence);
void write_block(u8 fig_num, u8 block, const u8* to_write_buf, std::array<u8, 32>& reply_buf, u8 sequence); void write_block(u8 fig_num, u8 block, const u8* to_write_buf, std::array<u8, 32>& reply_buf, u8 sequence);
void get_figure_identifier(u8 fig_num, u8 sequence, std::array<u8, 32>& reply_buf); void get_figure_identifier(u8 fig_num, u8 sequence, std::array<u8, 32>& reply_buf);
bool has_figure_been_added_removed() const; std::optional<std::array<u8, 32>> pop_added_removed_response();
std::array<u8, 32> pop_added_removed_response();
bool remove_figure(u8 position); bool remove_figure(u8 position);
u32 load_figure(const std::array<u8, 0x14 * 0x10>& buf, fs::file in_file, u8 position); u32 load_figure(const std::array<u8, 0x14 * 0x10>& buf, fs::file in_file, u8 position);

291
rpcs3/Emu/Io/KamenRider.cpp Normal file
View file

@ -0,0 +1,291 @@
#include "stdafx.h"
#include "KamenRider.h"
LOG_CHANNEL(kamen_rider_log, "kamen_rider");
rider_gate g_ridergate;
void kamen_rider_figure::save()
{
if (!kamen_file)
{
kamen_rider_log.error("Tried to save kamen rider figure to file but no kamen rider figure is active!");
return;
}
kamen_file.seek(0, fs::seek_set);
kamen_file.write(data.data(), 0x14 * 0x10);
}
u8 rider_gate::generate_checksum(const std::array<u8, 64>& data, u32 num_of_bytes) const
{
ensure(num_of_bytes <= data.size());
int checksum = 0;
for (u32 i = 0; i < num_of_bytes; i++)
{
checksum += data[i];
}
return (checksum & 0xFF);
}
kamen_rider_figure& rider_gate::get_figure_by_uid(const std::array<u8, 7> uid)
{
for (kamen_rider_figure& figure : figures)
{
if (figure.uid == uid)
{
return figure;
}
}
return figures[7];
}
void rider_gate::get_blank_response(u8 command, u8 sequence, std::array<u8, 64>& reply_buf)
{
reply_buf = {0x55, 0x02, command, sequence};
reply_buf[4] = generate_checksum(reply_buf, 4);
}
void rider_gate::wake_rider_gate(std::array<u8, 64>& reply_buf, u8 command, u8 sequence)
{
std::lock_guard lock(kamen_mutex);
m_is_awake = true;
reply_buf = {0x55, 0x1a, command, sequence, 0x00, 0x07, 0x00, 0x03, 0x02,
0x09, 0x20, 0x03, 0xf5, 0x00, 0x19, 0x42, 0x52, 0xb7,
0xb9, 0xa1, 0xae, 0x2b, 0x88, 0x42, 0x05, 0xfe, 0xe0, 0x1c, 0xac};
}
void rider_gate::get_list_tags(std::array<u8, 64>& reply_buf, u8 command, u8 sequence)
{
std::lock_guard lock(kamen_mutex);
reply_buf = {0x55, 0x02, command, sequence};
u8 index = 4;
for (const kamen_rider_figure& figure : figures)
{
if (figure.present)
{
reply_buf[index] = 0x09;
memcpy(&reply_buf[index + 1], figure.data.data(), 7);
index += 8;
reply_buf[1] += 8;
}
}
reply_buf[index] = generate_checksum(reply_buf, index);
}
void rider_gate::query_block(std::array<u8, 64>& reply_buf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block)
{
std::lock_guard lock(kamen_mutex);
reply_buf = {0x55, 0x13, command, sequence, 0x00};
const std::array<u8, 7> uid_array = {uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6]};
const kamen_rider_figure& figure = get_figure_by_uid(uid_array);
if (figure.present)
{
if (sector < 5 && block < 4)
{
memcpy(&reply_buf[5], &figure.data[(sector * 4 * 16) + (block * 16)], 16);
}
}
reply_buf[21] = generate_checksum(reply_buf, 21);
}
void rider_gate::write_block(std::array<u8, 64>& replyBuf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block, const u8* to_write_buf)
{
std::lock_guard lock(kamen_mutex);
const std::array<u8, 7> uid_array = {uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6]};
kamen_rider_figure& figure = get_figure_by_uid(uid_array);
if (figure.present)
{
if (sector < 5 && block < 4)
{
memcpy(&figure.data[(sector * 4 * 16) + (block * 16)], to_write_buf, 16);
}
}
get_blank_response(command, sequence, replyBuf);
}
std::optional<std::array<u8, 64>> rider_gate::pop_added_removed_response()
{
std::lock_guard lock(kamen_mutex);
if (m_figure_added_removed_responses.empty())
{
return std::nullopt;
}
std::array<u8, 64> response = m_figure_added_removed_responses.front();
m_figure_added_removed_responses.pop();
return response;
}
bool rider_gate::remove_figure(u8 index)
{
std::lock_guard lock(kamen_mutex);
auto& figure = figures[index];
if (figure.present)
{
figure.present = false;
figure.save();
figure.kamen_file.close();
if (m_is_awake)
{
std::array<u8, 64> figure_removed_response = {0x56, 0x09, 0x09, 0x00};
memcpy(&figure_removed_response[4], figure.uid.data(), figure.uid.size());
figure_removed_response[11] = generate_checksum(figure_removed_response, 11);
m_figure_added_removed_responses.push(std::move(figure_removed_response));
}
figure.uid = {};
return true;
}
return false;
}
u8 rider_gate::load_figure(const std::array<u8, 0x14 * 0x10>& buf, fs::file in_file)
{
std::lock_guard lock(kamen_mutex);
u8 found_slot = 0xFF;
// mimics spot retaining on the portal
for (auto i = 0; i < 7; i++)
{
if (!figures[i].present)
{
if (i < found_slot)
{
found_slot = i;
}
}
}
if (found_slot != 0xFF)
{
auto& figure = figures[found_slot];
memcpy(figure.data.data(), buf.data(), buf.size());
figure.kamen_file = std::move(in_file);
figure.uid = {buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]};
figure.present = true;
if (m_is_awake)
{
std::array<u8, 64> figure_added_response = {0x56, 0x09, 0x09, 0x01};
memcpy(&figure_added_response[4], figure.uid.data(), figure.uid.size());
figure_added_response[11] = generate_checksum(figure_added_response, 11);
m_figure_added_removed_responses.push(std::move(figure_added_response));
}
}
return found_slot;
}
usb_device_kamen_rider::usb_device_kamen_rider(const std::array<u8, 7>& location)
: usb_device_emulated(location)
{
device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{0x200, 0x0, 0x0, 0x0, 0x40, 0x0E6F, 0x200A, 0x100, 0x1, 0x2, 0x3, 0x1});
auto& config0 = device.add_node(UsbDescriptorNode(USB_DESCRIPTOR_CONFIG, UsbDeviceConfiguration{0x29, 0x1, 0x1, 0x0, 0x80, 0xFA}));
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, UsbDeviceInterface{0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0}));
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x81, 0x3, 0x40, 0x1}));
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x1, 0x3, 0x40, 0x1}));
}
usb_device_kamen_rider::~usb_device_kamen_rider()
{
}
std::shared_ptr<usb_device> usb_device_kamen_rider::make_instance(u32, const std::array<u8, 7>& location)
{
return std::make_shared<usb_device_kamen_rider>(location);
}
u16 usb_device_kamen_rider::get_num_emu_devices()
{
return 1;
}
void usb_device_kamen_rider::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer)
{
usb_device_emulated::control_transfer(bmRequestType, bRequest, wValue, wIndex, wLength, buf_size, buf, transfer);
}
void usb_device_kamen_rider::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer)
{
ensure(buf_size == 0x40);
transfer->fake = true;
transfer->expected_count = buf_size;
transfer->expected_result = HC_CC_NOERR;
if (endpoint == 0x81)
{
// Respond after FF command
transfer->expected_time = get_timestamp() + 1000;
std::optional<std::array<u8, 64>> response = g_ridergate.pop_added_removed_response();
if (response)
{
memcpy(buf, response.value().data(), 0x40);
}
else if (!m_queries.empty())
{
memcpy(buf, m_queries.front().data(), 0x20);
m_queries.pop();
}
else
{
transfer->expected_count = 0;
transfer->expected_result = EHCI_CC_HALTED;
}
}
else if (endpoint == 0x01)
{
const u8 command = buf[2];
const u8 sequence = buf[3];
std::array<u8, 64> q_result{};
switch (command)
{
case 0xB0: // Wake
{
g_ridergate.wake_rider_gate(q_result, command, sequence);
break;
}
case 0xC0:
case 0xC3: // Color Commands
{
g_ridergate.get_blank_response(command, sequence, q_result);
break;
}
case 0xD0: // Tag List
{
// Return list of figure UIDs, separated by an 09
g_ridergate.get_list_tags(q_result, command, sequence);
break;
}
case 0xD2: // Read
{
// Read 16 bytes from figure with UID buf[4] - buf[10]
g_ridergate.query_block(q_result, command, sequence, &buf[4], buf[11], buf[12]);
break;
}
case 0xD3:
{
// Write 16 bytes to figure with UID buf[4] - buf[10]
g_ridergate.write_block(q_result, command, sequence, &buf[4], buf[11], buf[12], &buf[13]);
break;
}
default:
kamen_rider_log.error("Unhandled Query Type: 0x%02X", command);
break;
}
m_queries.push(std::move(q_result));
}
}

60
rpcs3/Emu/Io/KamenRider.h Normal file
View file

@ -0,0 +1,60 @@
#pragma once
#include "Emu/Io/usb_device.h"
#include "Utilities/mutex.h"
#include <array>
#include <optional>
#include <queue>
struct kamen_rider_figure
{
fs::file kamen_file;
std::array<u8, 0x14 * 0x10> data{};
std::array<u8, 7> uid{};
bool present = false;
void save();
};
class rider_gate
{
public:
void get_blank_response(u8 command, u8 sequence, std::array<u8, 64>& reply_buf);
void wake_rider_gate(std::array<u8, 64>& replyBuf, u8 command, u8 sequence);
void get_list_tags(std::array<u8, 64>& replyBuf, u8 command, u8 sequence);
void query_block(std::array<u8, 64>& replyBuf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block);
void write_block(std::array<u8, 64>& replyBuf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block, const u8* to_write_buf);
std::optional<std::array<u8, 64>> pop_added_removed_response();
bool remove_figure(u8 position);
u8 load_figure(const std::array<u8, 0x14 * 0x10>& buf, fs::file in_file);
protected:
shared_mutex kamen_mutex;
std::array<kamen_rider_figure, 8> figures{};
private:
u8 generate_checksum(const std::array<u8, 64>& data, u32 num_of_bytes) const;
kamen_rider_figure& get_figure_by_uid(const std::array<u8, 7> uid);
std::queue<std::array<u8, 64>> m_figure_added_removed_responses;
bool m_is_awake = false;
};
extern rider_gate g_ridergate;
class usb_device_kamen_rider : public usb_device_emulated
{
public:
usb_device_kamen_rider(const std::array<u8, 7>& location);
~usb_device_kamen_rider();
static std::shared_ptr<usb_device> make_instance(u32 controller_index, const std::array<u8, 7>& location);
static u16 get_num_emu_devices();
void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override;
void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override;
protected:
std::queue<std::array<u8, 64>> m_queries;
};

View file

@ -170,7 +170,7 @@ u16 PadHandlerBase::ConvertAxis(f32 value)
// The DS3, (and i think xbox controllers) give a 'square-ish' type response, so that the corners will give (almost)max x/y instead of the ~30x30 from a perfect circle // The DS3, (and i think xbox controllers) give a 'square-ish' type response, so that the corners will give (almost)max x/y instead of the ~30x30 from a perfect circle
// using a simple scale/sensitivity increase would *work* although it eats a chunk of our usable range in exchange // using a simple scale/sensitivity increase would *work* although it eats a chunk of our usable range in exchange
// this might be the best for now, in practice it seems to push the corners to max of 20x20, with a squircle_factor of 8000 // this might be the best for now, in practice it seems to push the corners to max of 20x20, with a squircle_factor of ~4000
// This function assumes inX and inY is already in 0-255 // This function assumes inX and inY is already in 0-255
void PadHandlerBase::ConvertToSquirclePoint(u16& inX, u16& inY, u32 squircle_factor) void PadHandlerBase::ConvertToSquirclePoint(u16& inX, u16& inY, u32 squircle_factor)
{ {

View file

@ -88,8 +88,8 @@ struct cfg_pad final : cfg::node
cfg::uint<0, 1000000> rstick_anti_deadzone{ this, "Right Stick Anti-Deadzone", 0 }; cfg::uint<0, 1000000> rstick_anti_deadzone{ this, "Right Stick Anti-Deadzone", 0 };
cfg::uint<0, 1000000> ltriggerthreshold{ this, "Left Trigger Threshold", 0 }; cfg::uint<0, 1000000> ltriggerthreshold{ this, "Left Trigger Threshold", 0 };
cfg::uint<0, 1000000> rtriggerthreshold{ this, "Right Trigger Threshold", 0 }; cfg::uint<0, 1000000> rtriggerthreshold{ this, "Right Trigger Threshold", 0 };
cfg::uint<0, 1000000> lpadsquircling{ this, "Left Pad Squircling Factor", 8000 }; cfg::uint<0, 1000000> lpadsquircling{ this, "Left Pad Squircling Factor", 4000 };
cfg::uint<0, 1000000> rpadsquircling{ this, "Right Pad Squircling Factor", 8000 }; cfg::uint<0, 1000000> rpadsquircling{ this, "Right Pad Squircling Factor", 4000 };
cfg::uint<0, 255> colorR{ this, "Color Value R", 0 }; cfg::uint<0, 255> colorR{ this, "Color Value R", 0 };
cfg::uint<0, 255> colorG{ this, "Color Value G", 0 }; cfg::uint<0, 255> colorG{ this, "Color Value G", 0 };

View file

@ -217,6 +217,12 @@ bool Pad::get_analog_limiter_button_active(bool is_toggle_mode, u32 player_id)
const Button& analog_limiter_button = m_buttons[m_analog_limiter_button_index]; const Button& analog_limiter_button = m_buttons[m_analog_limiter_button_index];
if (analog_limiter_button.m_key_codes.empty())
{
// Active by default if no button was assigned
return true;
}
if (is_toggle_mode) if (is_toggle_mode)
{ {
const bool pressed = analog_limiter_button.m_pressed; const bool pressed = analog_limiter_button.m_pressed;

View file

@ -950,7 +950,7 @@ namespace vm
return true; return true;
} }
static u32 _page_unmap(u32 addr, u32 max_size, u64 bflags, utils::shm* shm, std::vector<std::pair<u64, u64>>& unmap_events) static u32 _page_unmap(u32 addr, u32 max_size, u64 bflags, utils::shm* shm, std::vector<std::pair<u64, u64>>& unmap_events, bool is_block_termination = false)
{ {
perf_meter<"PAGE_UNm"_u64> perf0; perf_meter<"PAGE_UNm"_u64> perf0;
@ -1021,7 +1021,11 @@ namespace vm
ppu_remove_hle_instructions(addr, size); ppu_remove_hle_instructions(addr, size);
// Actually unmap memory // Actually unmap memory
if (is_noop) if (is_block_termination && (!shm || is_noop))
{
// We can skip it if the block is freed
}
else if (is_noop)
{ {
std::memset(g_sudo_addr + addr, 0, size); std::memset(g_sudo_addr + addr, 0, size);
} }
@ -1327,7 +1331,7 @@ namespace vm
const auto size = it->second.first; const auto size = it->second.first;
std::vector<std::pair<u64, u64>> event_data; std::vector<std::pair<u64, u64>> event_data;
ensure(size == _page_unmap(it->first, size, this->flags, it->second.second.get(), unmapped ? *unmapped : event_data)); ensure(size == _page_unmap(it->first, size, this->flags, it->second.second.get(), unmapped ? *unmapped : event_data, true));
if (it->second.second && addr < 0xE0000000) if (it->second.second && addr < 0xE0000000)
{ {

View file

@ -646,7 +646,7 @@ namespace np
for (; it != end; ++it) for (; it != end; ++it)
{ {
strcpy(ifr.ifr_name, it->ifr_name); strcpy_trunc(ifr.ifr_name, it->ifr_name);
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0) if (ioctl(sock, SIOCGIFFLAGS, &ifr) == 0)
{ {
if (!(ifr.ifr_flags & IFF_LOOPBACK)) if (!(ifr.ifr_flags & IFF_LOOPBACK))

View file

@ -40,10 +40,15 @@ namespace utils
/** /**
* Stream a 128 bits vector from src to dst. * Stream a 128 bits vector from src to dst.
*/ */
static inline template <int Count = 1>
void stream_vector_from_memory(void* dst, void* src) void stream_vector_from_memory(void* dst, void* src)
{ {
const __m128i vector = _mm_loadu_si128(reinterpret_cast<__m128i*>(src)); auto _src = reinterpret_cast<__m128i*>(src);
_mm_stream_si128(reinterpret_cast<__m128i*>(dst), vector); auto _dst = reinterpret_cast<__m128i*>(dst);
for (int i = 0; i < Count; ++i, ++_src, ++_dst)
{
const __m128i vector = _mm_loadu_si128(_src);
_mm_stream_si128(_dst, vector);
}
} }
} }

View file

@ -1869,9 +1869,10 @@ namespace rsx
{ {
return{ cached_texture->get_view(remap), cached_texture->get_context(), cached_texture->get_format_class(), scale, cached_texture->get_image_type() }; return{ cached_texture->get_view(remap), cached_texture->get_context(), cached_texture->get_format_class(), scale, cached_texture->get_image_type() };
} }
return {};
} }
else
{
// Fast lookup for cyclic reference // Fast lookup for cyclic reference
if (m_rtts.address_is_bound(attr.address)) [[unlikely]] if (m_rtts.address_is_bound(attr.address)) [[unlikely]]
{ {
@ -2093,9 +2094,13 @@ namespace rsx
return {}; return {};
} }
if (const auto section_count = result.external_subresource_desc.sections_to_copy.size(); const auto section_count = result.external_subresource_desc.sections_to_copy.size();
section_count > 0) if (section_count == 0)
{ {
// Fail
return {};
}
bool result_is_valid; bool result_is_valid;
if (_pool == 0 && !g_cfg.video.write_color_buffers && !g_cfg.video.write_depth_buffer) if (_pool == 0 && !g_cfg.video.write_color_buffers && !g_cfg.video.write_depth_buffer)
{ {
@ -2111,8 +2116,11 @@ namespace rsx
result_is_valid = result.atlas_covers_target_area(section_count == 1 ? 99 : 90); result_is_valid = result.atlas_covers_target_area(section_count == 1 ? 99 : 90);
} }
if (result_is_valid) if (!result_is_valid)
{ {
return {};
}
// Check for possible duplicates // Check for possible duplicates
usz max_overdraw_ratio = u32{ umax }; usz max_overdraw_ratio = u32{ umax };
usz max_safe_sections = u32{ umax }; usz max_safe_sections = u32{ umax };
@ -2175,9 +2183,6 @@ namespace rsx
return result; return result;
} }
}
}
}
return {}; return {};
} }

View file

@ -262,18 +262,29 @@ namespace rsx
u64 tag; // Timestamp u64 tag; // Timestamp
u32 list; // List source, 0 = fbo, 1 = local u32 list; // List source, 0 = fbo, 1 = local
u32 index; // Index in list u32 index; // Index in list
utils::address_range32 bounds;
}; };
std::vector<sort_helper> sort_list; const u32 available_slices = fbos.size() + local.size();
bool unordered_list = false;
rsx::simple_array<sort_helper> sort_list;
rsx::simple_array<utils::address_range32> sort_ranges;
sort_list.reserve(available_slices);
sort_ranges.reserve(available_slices);
if (!fbos.empty() && !local.empty())
{
// Generate sorting tree if both resources are available and overlapping // Generate sorting tree if both resources are available and overlapping
sort_list.reserve(fbos.size() + local.size());
for (u32 index = 0; index < fbos.size(); ++index) for (u32 index = 0; index < fbos.size(); ++index)
{ {
sort_list.push_back({ fbos[index].surface->last_use_tag, 0, index }); const auto range = fbos[index].surface->get_memory_range();
sort_ranges.push_back(range);
sort_list.push_back({
.tag = fbos[index].surface->last_use_tag,
.list = 0,
.index = index,
.bounds = range
});
} }
for (u32 index = 0; index < local.size(); ++index) for (u32 index = 0; index < local.size(); ++index)
@ -281,13 +292,37 @@ namespace rsx
if (local[index]->get_context() != rsx::texture_upload_context::blit_engine_dst) if (local[index]->get_context() != rsx::texture_upload_context::blit_engine_dst)
continue; continue;
sort_list.push_back({ local[index]->last_write_tag, 1, index }); const auto range = local[index]->get_section_range();
sort_ranges.push_back(range);
sort_list.push_back({
.tag = local[index]->last_write_tag,
.list = 1,
.index = index,
.bounds = range
});
} }
std::sort(sort_list.begin(), sort_list.end(), FN(x.tag < y.tag)); if (!fbos.empty() && !local.empty())
{
sort_list.sort(FN(x.tag < y.tag));
} }
auto add_rtt_resource = [&](auto& section, u16 slice) // Check if ordered
for (u32 i = 0; i < sort_list.size(); ++i)
{
if (i == 0)
{
continue;
}
if (sort_ranges[i].start < sort_ranges[i - 1].end)
{
unordered_list = true;
break;
}
}
auto add_rtt_resource = [&](auto& section, u16 slice) -> std::pair<bool, bool> // [ input fully consumed, output fully covered ]
{ {
const u32 slice_begin = (slice * attr.slice_h); const u32 slice_begin = (slice * attr.slice_h);
const u32 slice_end = (slice_begin + attr.height); const u32 slice_end = (slice_begin + attr.height);
@ -296,7 +331,7 @@ namespace rsx
if (section.dst_area.y >= slice_end || section_end <= slice_begin) if (section.dst_area.y >= slice_end || section_end <= slice_begin)
{ {
// Belongs to a different slice // Belongs to a different slice
return; return { section_end <= slice_begin, false };
} }
// How much of this slice to read? // How much of this slice to read?
@ -346,9 +381,11 @@ namespace rsx
.dst_w = dst_width, .dst_w = dst_width,
.dst_h = dst_height .dst_h = dst_height
}); });
return { section_end <= slice_end, section_end >= slice_end };
}; };
auto add_local_resource = [&](auto& section, u32 address, u16 slice, bool scaling = true) auto add_local_resource = [&](auto& section, u32 address, u16 slice, bool scaling) -> std::pair<bool, bool> // [ input fully consumed, output fully covered ]
{ {
// Intersect this resource with the original one. // Intersect this resource with the original one.
// Note that intersection takes place in a normalized coordinate space (bpp = 1) // Note that intersection takes place in a normalized coordinate space (bpp = 1)
@ -364,7 +401,7 @@ namespace rsx
if (!dimensions.width || !dimensions.height) if (!dimensions.width || !dimensions.height)
{ {
// Out of bounds, invalid intersection // Out of bounds, invalid intersection
return; return { false, false };
} }
// The intersection takes place in a normalized coordinate space. Now we convert back to domain-specific // The intersection takes place in a normalized coordinate space. Now we convert back to domain-specific
@ -383,7 +420,7 @@ namespace rsx
if (dst_y >= dst_slice_end || write_section_end <= dst_slice_begin) if (dst_y >= dst_slice_end || write_section_end <= dst_slice_begin)
{ {
// Belongs to a different slice // Belongs to a different slice
return; return { write_section_end <= dst_slice_begin, false };
} }
const u16 dst_w = static_cast<u16>(dst_size.width); const u16 dst_w = static_cast<u16>(dst_size.width);
@ -411,9 +448,10 @@ namespace rsx
.dst_w = _dst_w, .dst_w = _dst_w,
.dst_h = _dst_h .dst_h = _dst_h
}); });
return { write_section_end <= dst_slice_end, write_section_end >= dst_slice_end };
} }
else
{
out.push_back out.push_back
({ ({
.src = section->get_raw_texture(), .src = section->get_raw_texture(),
@ -429,7 +467,8 @@ namespace rsx
.dst_w = dst_w, .dst_w = dst_w,
.dst_h = height .dst_h = height
}); });
}
return { write_section_end <= dst_slice_end, write_section_end >= dst_slice_end };
}; };
u32 current_address = attr.address; u32 current_address = attr.address;
@ -442,34 +481,36 @@ namespace rsx
for (u16 slice = 0; slice < count; ++slice) for (u16 slice = 0; slice < count; ++slice)
{ {
auto num_surface = out.size(); const auto num_surface = out.size();
const auto slice_range = utils::address_range32::start_length(current_address, slice_size);
if (local.empty()) [[likely]] for (auto& e : sort_list)
{ {
for (auto& section : fbos) if (e.index == umax || !slice_range.overlaps(e.bounds))
{ {
add_rtt_resource(section, slice); continue;
} }
}
else if (fbos.empty()) bool remove = false, slice_complete = false;
{
for (auto& section : local)
{
add_local_resource(section, current_address, slice, false);
}
}
else
{
for (const auto& e : sort_list)
{
if (e.list == 0) if (e.list == 0)
{ {
add_rtt_resource(fbos[e.index], slice); std::tie(remove, slice_complete) = add_rtt_resource(fbos[e.index], slice);
} }
else else
{ {
add_local_resource(local[e.index], current_address, slice); std::tie(remove, slice_complete) = add_local_resource(local[e.index], current_address, slice, !fbos.empty());
} }
if (remove)
{
// If we got here, the section has been fully ingested by the current slice and will never match again.
e.index = umax;
}
if (slice_complete && !unordered_list)
{
// Reached the end of the current slice and we are guaranteed to not match any other section since they lie after this one in memory.
break;
} }
} }

View file

@ -664,7 +664,21 @@ namespace rsx
void draw_command_processor::fill_fragment_state_buffer(void* buffer, const RSXFragmentProgram& /*fragment_program*/) const void draw_command_processor::fill_fragment_state_buffer(void* buffer, const RSXFragmentProgram& /*fragment_program*/) const
{ {
#pragma pack(push, 1)
struct fragment_context_t
{
f32 fog_param0;
f32 fog_param1;
u32 rop_control;
f32 alpha_ref;
u32 fog_mode;
f32 wpos_scale;
f32 wpos_bias[2];
};
#pragma pack(pop)
ROP_control_t rop_control{}; ROP_control_t rop_control{};
alignas(16) fragment_context_t payload{};
if (REGS(m_ctx)->alpha_test_enabled()) if (REGS(m_ctx)->alpha_test_enabled())
{ {
@ -720,10 +734,6 @@ namespace rsx
} }
} }
const f32 fog0 = REGS(m_ctx)->fog_params_0();
const f32 fog1 = REGS(m_ctx)->fog_params_1();
const u32 fog_mode = static_cast<u32>(REGS(m_ctx)->fog_equation());
// Check if framebuffer is actually an XRGB format and not a WZYX format // Check if framebuffer is actually an XRGB format and not a WZYX format
switch (REGS(m_ctx)->surface_color()) switch (REGS(m_ctx)->surface_color())
{ {
@ -745,21 +755,37 @@ namespace rsx
} }
// Generate wpos coefficients // Generate wpos coefficients
// wpos equation is now as follows: // wpos equation is now as follows (ignoring pixel center offset):
// wpos.y = (frag_coord / resolution_scale) * ((window_origin!=top)?-1.: 1.) + ((window_origin!=top)? window_height : 0) // wpos.y = (frag_coord / resolution_scale) * ((window_origin!=top)?-1.: 1.) + ((window_origin!=top)? window_height : 0)
// wpos.x = (frag_coord / resolution_scale) // wpos.x = (frag_coord / resolution_scale)
// wpos.zw = frag_coord.zw // wpos.zw = frag_coord.zw
payload.fog_param0 = REGS(m_ctx)->fog_params_0();
payload.fog_param1 = REGS(m_ctx)->fog_params_1();
payload.fog_mode = static_cast<u32>(REGS(m_ctx)->fog_equation());
payload.rop_control = rop_control.value;
payload.alpha_ref = REGS(m_ctx)->alpha_ref();
const auto window_origin = REGS(m_ctx)->shader_window_origin(); const auto window_origin = REGS(m_ctx)->shader_window_origin();
const u32 window_height = REGS(m_ctx)->shader_window_height(); const u32 window_height = REGS(m_ctx)->shader_window_height();
const auto pixel_center = REGS(m_ctx)->pixel_center();
const f32 resolution_scale = (window_height <= static_cast<u32>(g_cfg.video.min_scalable_dimension)) ? 1.f : rsx::get_resolution_scale(); const f32 resolution_scale = (window_height <= static_cast<u32>(g_cfg.video.min_scalable_dimension)) ? 1.f : rsx::get_resolution_scale();
const f32 wpos_scale = (window_origin == rsx::window_origin::top) ? (1.f / resolution_scale) : (-1.f / resolution_scale);
const f32 wpos_bias = (window_origin == rsx::window_origin::top) ? 0.f : window_height;
const f32 alpha_ref = REGS(m_ctx)->alpha_ref();
u32* dst = static_cast<u32*>(buffer); payload.wpos_scale = (window_origin == rsx::window_origin::top) ? (1.f / resolution_scale) : (-1.f / resolution_scale);
utils::stream_vector(dst, std::bit_cast<u32>(fog0), std::bit_cast<u32>(fog1), rop_control.value, std::bit_cast<u32>(alpha_ref)); payload.wpos_bias[0] = 0.f;
utils::stream_vector(dst + 4, 0u, fog_mode, std::bit_cast<u32>(wpos_scale), std::bit_cast<u32>(wpos_bias)); payload.wpos_bias[1] = (window_origin == rsx::window_origin::top) ? 0.f : window_height;
if (pixel_center == window_pixel_center::integer)
{
// We could technically fix this shader side, but...
// 1. We have full control over gl_FragCoord consumption, so fix it using our own pipeline as it is an emulated input.
// 2. Vulkan does not support pixel_center_integer decoration. SPIR-V modules only permit pixel center at half offset.
payload.wpos_bias[0] -= 0.5f;
payload.wpos_bias[1] -= 0.5f;
}
utils::stream_vector_from_memory<2>(buffer, &payload);
} }
void draw_command_processor::fill_constants_instancing_buffer(rsx::io_buffer& indirection_table_buf, rsx::io_buffer& constants_data_array_buffer, const VertexProgramBase* prog) const void draw_command_processor::fill_constants_instancing_buffer(rsx::io_buffer& indirection_table_buf, rsx::io_buffer& constants_data_array_buffer, const VertexProgramBase* prog) const

View file

@ -7,36 +7,14 @@
namespace gl namespace gl
{ {
GLenum comparison_op(rsx::comparison_function op) inline GLenum comparison_op(rsx::comparison_function op)
{ {
switch (op) return static_cast<GLenum>(op);
{
case rsx::comparison_function::never: return GL_NEVER;
case rsx::comparison_function::less: return GL_LESS;
case rsx::comparison_function::equal: return GL_EQUAL;
case rsx::comparison_function::less_or_equal: return GL_LEQUAL;
case rsx::comparison_function::greater: return GL_GREATER;
case rsx::comparison_function::not_equal: return GL_NOTEQUAL;
case rsx::comparison_function::greater_or_equal: return GL_GEQUAL;
case rsx::comparison_function::always: return GL_ALWAYS;
}
fmt::throw_exception("Unsupported comparison op 0x%X", static_cast<u32>(op));
} }
GLenum stencil_op(rsx::stencil_op op) inline GLenum stencil_op(rsx::stencil_op op)
{ {
switch (op) return static_cast<GLenum>(op);
{
case rsx::stencil_op::invert: return GL_INVERT;
case rsx::stencil_op::keep: return GL_KEEP;
case rsx::stencil_op::zero: return GL_ZERO;
case rsx::stencil_op::replace: return GL_REPLACE;
case rsx::stencil_op::incr: return GL_INCR;
case rsx::stencil_op::decr: return GL_DECR;
case rsx::stencil_op::incr_wrap: return GL_INCR_WRAP;
case rsx::stencil_op::decr_wrap: return GL_DECR_WRAP;
}
fmt::throw_exception("Unsupported stencil op 0x%X", static_cast<u32>(op));
} }
GLenum blend_equation(rsx::blend_equation op) GLenum blend_equation(rsx::blend_equation op)
@ -62,76 +40,33 @@ namespace gl
} }
} }
GLenum blend_factor(rsx::blend_factor op) inline GLenum blend_factor(rsx::blend_factor op)
{ {
switch (op) return static_cast<GLenum>(op);
{
case rsx::blend_factor::zero: return GL_ZERO;
case rsx::blend_factor::one: return GL_ONE;
case rsx::blend_factor::src_color: return GL_SRC_COLOR;
case rsx::blend_factor::one_minus_src_color: return GL_ONE_MINUS_SRC_COLOR;
case rsx::blend_factor::dst_color: return GL_DST_COLOR;
case rsx::blend_factor::one_minus_dst_color: return GL_ONE_MINUS_DST_COLOR;
case rsx::blend_factor::src_alpha: return GL_SRC_ALPHA;
case rsx::blend_factor::one_minus_src_alpha: return GL_ONE_MINUS_SRC_ALPHA;
case rsx::blend_factor::dst_alpha: return GL_DST_ALPHA;
case rsx::blend_factor::one_minus_dst_alpha: return GL_ONE_MINUS_DST_ALPHA;
case rsx::blend_factor::src_alpha_saturate: return GL_SRC_ALPHA_SATURATE;
case rsx::blend_factor::constant_color: return GL_CONSTANT_COLOR;
case rsx::blend_factor::one_minus_constant_color: return GL_ONE_MINUS_CONSTANT_COLOR;
case rsx::blend_factor::constant_alpha: return GL_CONSTANT_ALPHA;
case rsx::blend_factor::one_minus_constant_alpha: return GL_ONE_MINUS_CONSTANT_ALPHA;
}
fmt::throw_exception("Unsupported blend factor 0x%X", static_cast<u32>(op));
} }
GLenum logic_op(rsx::logic_op op) inline GLenum logic_op(rsx::logic_op op)
{ {
switch (op) return static_cast<GLenum>(op);
{
case rsx::logic_op::logic_clear: return GL_CLEAR;
case rsx::logic_op::logic_and: return GL_AND;
case rsx::logic_op::logic_and_reverse: return GL_AND_REVERSE;
case rsx::logic_op::logic_copy: return GL_COPY;
case rsx::logic_op::logic_and_inverted: return GL_AND_INVERTED;
case rsx::logic_op::logic_noop: return GL_NOOP;
case rsx::logic_op::logic_xor: return GL_XOR;
case rsx::logic_op::logic_or: return GL_OR;
case rsx::logic_op::logic_nor: return GL_NOR;
case rsx::logic_op::logic_equiv: return GL_EQUIV;
case rsx::logic_op::logic_invert: return GL_INVERT;
case rsx::logic_op::logic_or_reverse: return GL_OR_REVERSE;
case rsx::logic_op::logic_copy_inverted: return GL_COPY_INVERTED;
case rsx::logic_op::logic_or_inverted: return GL_OR_INVERTED;
case rsx::logic_op::logic_nand: return GL_NAND;
case rsx::logic_op::logic_set: return GL_SET;
}
fmt::throw_exception("Unsupported logic op 0x%X", static_cast<u32>(op));
} }
GLenum front_face(rsx::front_face op) inline GLenum front_face(rsx::front_face op)
{ {
//NOTE: RSX face winding is always based off of upper-left corner like vulkan, but GL is bottom left // NOTE: RSX face winding is always based off of upper-left corner like vulkan, but GL is bottom left
//shader_window_origin register does not affect this // shader_window_origin register does not affect this
//verified with Outrun Online Arcade (window_origin::top) and DS2 (window_origin::bottom) // verified with Outrun Online Arcade (window_origin::top) and DS2 (window_origin::bottom)
//correctness of face winding checked using stencil test (GOW collection shadows) // correctness of face winding checked using stencil test (GOW collection shadows)
switch (op) return static_cast<GLenum>(op) ^ 1u;
{
case rsx::front_face::cw: return GL_CCW;
case rsx::front_face::ccw: return GL_CW;
}
fmt::throw_exception("Unsupported front face 0x%X", static_cast<u32>(op));
} }
GLenum cull_face(rsx::cull_face op) inline GLenum cull_face(rsx::cull_face op)
{ {
switch (op) return static_cast<GLenum>(op);
{
case rsx::cull_face::front: return GL_FRONT;
case rsx::cull_face::back: return GL_BACK;
case rsx::cull_face::front_and_back: return GL_FRONT_AND_BACK;
} }
fmt::throw_exception("Unsupported cull face 0x%X", static_cast<u32>(op));
inline GLenum polygon_mode(rsx::polygon_mode mode)
{
return static_cast<GLenum>(mode);
} }
} }
@ -335,6 +270,10 @@ void GLGSRender::update_draw_state()
// Clip planes // Clip planes
gl_state.clip_planes((current_vertex_program.output_mask >> CELL_GCM_ATTRIB_OUTPUT_UC0) & 0x3F); gl_state.clip_planes((current_vertex_program.output_mask >> CELL_GCM_ATTRIB_OUTPUT_UC0) & 0x3F);
// Polygon mode. We can only have one polygon mode active at one time, so we need to determine if any face is currently culled.
const bool show_back = REGS(m_ctx)->cull_face_enabled() && REGS(m_ctx)->cull_face_mode() == rsx::cull_face::front;
gl_state.polygon_mode(gl::polygon_mode(show_back ? REGS(m_ctx)->polygon_mode_back() : REGS(m_ctx)->polygon_mode_front()));
//TODO //TODO
//NV4097_SET_ANISO_SPREAD //NV4097_SET_ANISO_SPREAD
//NV4097_SET_SPECULAR_ENABLE //NV4097_SET_SPECULAR_ENABLE
@ -342,9 +281,6 @@ void GLGSRender::update_draw_state()
//NV4097_SET_FLAT_SHADE_OP //NV4097_SET_FLAT_SHADE_OP
//NV4097_SET_EDGE_FLAG //NV4097_SET_EDGE_FLAG
//NV4097_SET_COLOR_KEY_COLOR //NV4097_SET_COLOR_KEY_COLOR
//NV4097_SET_SHADER_CONTROL
//NV4097_SET_ZMIN_MAX_CONTROL
//NV4097_SET_ANTI_ALIASING_CONTROL
//NV4097_SET_CLIP_ID_TEST_ENABLE //NV4097_SET_CLIP_ID_TEST_ENABLE
// For OGL Z range is updated every draw as it is separate from viewport config // For OGL Z range is updated every draw as it is separate from viewport config

View file

@ -175,10 +175,9 @@ void GLFragmentDecompilerThread::insertConstants(std::stringstream & OS)
" float fog_param1;\n" " float fog_param1;\n"
" uint rop_control;\n" " uint rop_control;\n"
" float alpha_ref;\n" " float alpha_ref;\n"
" uint reserved;\n"
" uint fog_mode;\n" " uint fog_mode;\n"
" float wpos_scale;\n" " float wpos_scale;\n"
" float wpos_bias;\n" " vec2 wpos_bias;\n"
"};\n\n" "};\n\n"
"layout(std140, binding = " << GL_FRAGMENT_TEXTURE_PARAMS_BIND_SLOT << ") uniform TextureParametersBuffer\n" "layout(std140, binding = " << GL_FRAGMENT_TEXTURE_PARAMS_BIND_SLOT << ") uniform TextureParametersBuffer\n"

View file

@ -1411,6 +1411,12 @@ void GLGSRender::on_guest_texture_read()
enqueue_host_context_write(::offset32(&rsx::host_gpu_context_t::texture_load_complete_event), 8, &event_id); enqueue_host_context_write(::offset32(&rsx::host_gpu_context_t::texture_load_complete_event), 8, &event_id);
} }
void GLGSRender::write_barrier(u32 address, u32 range)
{
ensure(is_current_thread());
m_rtts.invalidate_range(utils::address_range32::start_length(address, range));
}
void GLGSRender::begin_occlusion_query(rsx::reports::occlusion_query_info* query) void GLGSRender::begin_occlusion_query(rsx::reports::occlusion_query_info* query)
{ {
query->result = 0; query->result = 0;

View file

@ -192,8 +192,12 @@ public:
gl::work_item& post_flush_request(u32 address, gl::texture_cache::thrashed_set& flush_data); gl::work_item& post_flush_request(u32 address, gl::texture_cache::thrashed_set& flush_data);
// NV3089
bool scaled_image_from_memory(const rsx::blit_src_info& src_info, const rsx::blit_dst_info& dst_info, bool interpolate) override; bool scaled_image_from_memory(const rsx::blit_src_info& src_info, const rsx::blit_dst_info& dst_info, bool interpolate) override;
// Sync
void write_barrier(u32 address, u32 range) override;
// ZCULL // ZCULL
void begin_occlusion_query(rsx::reports::occlusion_query_info* query) override; void begin_occlusion_query(rsx::reports::occlusion_query_info* query) override;
void end_occlusion_query(rsx::reports::occlusion_query_info* query) override; void end_occlusion_query(rsx::reports::occlusion_query_info* query) override;

View file

@ -176,6 +176,8 @@ namespace gl
cmd->disablei(GL_BLEND, 0); cmd->disablei(GL_BLEND, 0);
} }
cmd->polygon_mode(GL_FILL);
// Render // Render
cmd->use_program(program_handle.id()); cmd->use_program(program_handle.id());
on_load(); on_load();

View file

@ -17,6 +17,7 @@ namespace gl
const u32 STENCIL_FRONT_OP = 0xFFFF0007; const u32 STENCIL_FRONT_OP = 0xFFFF0007;
const u32 STENCIL_BACK_OP = 0xFFFF0008; const u32 STENCIL_BACK_OP = 0xFFFF0008;
const u32 STENCIL_BACK_MASK = 0xFFFF0009; const u32 STENCIL_BACK_MASK = 0xFFFF0009;
const u32 POLYGON_MODE = 0xFFFF000A;
std::unordered_map<GLenum, u64> properties = {}; std::unordered_map<GLenum, u64> properties = {};
std::unordered_map<GLenum, std::array<u64, 4>> indexed_properties = {}; std::unordered_map<GLenum, std::array<u64, 4>> indexed_properties = {};
@ -353,6 +354,15 @@ namespace gl
} }
} }
void polygon_mode(GLenum mode)
{
if (!test_and_set_property(POLYGON_MODE, mode))
{
// Note: GL4+ does not support separate polygon mode per-face-type
glPolygonMode(GL_FRONT_AND_BACK, mode);
}
}
void use_program(GLuint program) void use_program(GLuint program)
{ {
if (current_program == program) if (current_program == program)

View file

@ -5,12 +5,44 @@
#include "Emu/RSX/Core/RSXReservationLock.hpp" #include "Emu/RSX/Core/RSXReservationLock.hpp"
#include "Emu/RSX/Host/MM.h" #include "Emu/RSX/Host/MM.h"
#include "Utilities/deferred_op.hpp"
#include "context_accessors.define.h" #include "context_accessors.define.h"
namespace rsx namespace rsx
{ {
namespace nv0039 namespace nv0039
{ {
// Transfer with stride
inline void block2d_copy_with_stride(u8* dst, const u8* src, u32 width, u32 height, u32 src_pitch, u32 dst_pitch, u8 src_stride, u8 dst_stride)
{
for (u32 row = 0; row < height; ++row)
{
auto dst_ptr = dst;
auto src_ptr = src;
while (src_ptr < src + width)
{
*dst_ptr = *src_ptr;
src_ptr += src_stride;
dst_ptr += dst_stride;
}
dst += dst_pitch;
src += src_pitch;
}
}
inline void block2d_copy(u8* dst, const u8* src, u32 width, u32 height, u32 src_pitch, u32 dst_pitch)
{
for (u32 i = 0; i < height; ++i)
{
std::memcpy(dst, src, width);
dst += dst_pitch;
src += src_pitch;
}
}
void buffer_notify(context* ctx, u32, u32 arg) void buffer_notify(context* ctx, u32, u32 arg)
{ {
s32 in_pitch = REGS(ctx)->nv0039_input_pitch(); s32 in_pitch = REGS(ctx)->nv0039_input_pitch();
@ -56,6 +88,13 @@ namespace rsx
} }
} }
// Deferred write_barrier on RSX side
utils::deferred_op deferred([&]()
{
RSX(ctx)->write_barrier(write_address, write_length);
// res->release(0);
});
auto res = ::rsx::reservation_lock<true>(write_address, write_length, read_address, read_length); auto res = ::rsx::reservation_lock<true>(write_address, write_length, read_address, read_length);
u8* dst = vm::_ptr<u8>(write_address); u8* dst = vm::_ptr<u8>(write_address);
@ -81,68 +120,34 @@ namespace rsx
// The formats are just input channel strides. You can use this to do cool tricks like gathering channels // The formats are just input channel strides. You can use this to do cool tricks like gathering channels
// Very rare, only seen in use by Destiny // Very rare, only seen in use by Destiny
// TODO: Hw accel // TODO: Hw accel
for (u32 row = 0; row < line_count; ++row) block2d_copy_with_stride(dst, src, line_length, line_count, in_pitch, out_pitch, in_format, out_format);
{ return;
auto dst_ptr = dst;
auto src_ptr = src;
while (src_ptr < src + line_length)
{
*dst_ptr = *src_ptr;
src_ptr += in_format;
dst_ptr += out_format;
} }
dst += out_pitch; if (!is_overlapping)
src += in_pitch;
}
}
else if (is_overlapping) [[ unlikely ]]
{
if (is_block_transfer)
{
std::memmove(dst, src, read_length);
}
else
{
std::vector<u8> temp(line_length * line_count);
u8* buf = temp.data();
for (u32 y = 0; y < line_count; ++y)
{
std::memcpy(buf, src, line_length);
buf += line_length;
src += in_pitch;
}
buf = temp.data();
for (u32 y = 0; y < line_count; ++y)
{
std::memcpy(dst, buf, line_length);
buf += line_length;
dst += out_pitch;
}
}
}
else
{ {
if (is_block_transfer) if (is_block_transfer)
{ {
std::memcpy(dst, src, read_length); std::memcpy(dst, src, read_length);
} return;
else
{
for (u32 i = 0; i < line_count; ++i)
{
std::memcpy(dst, src, line_length);
dst += out_pitch;
src += in_pitch;
}
}
} }
//res->release(0); block2d_copy(dst, src, line_length, line_count, in_pitch, out_pitch);
return;
}
if (is_block_transfer)
{
std::memmove(dst, src, read_length);
return;
}
// Handle overlapping 2D range using double-copy to temp.
std::vector<u8> temp(line_length * line_count);
u8* buf = temp.data();
block2d_copy(buf, src, line_length, line_count, in_pitch, line_length);
block2d_copy(dst, buf, line_length, line_count, line_length, out_pitch);
} }
} }
} }

View file

@ -35,18 +35,31 @@ namespace rsx
return page_navigation::exit; return page_navigation::exit;
}); });
if (!suspend_mode && boot_current_game_savestate(true, 1)) { for (u32 save_index = 1; !suspend_mode && save_index <= 4; save_index++)
std::unique_ptr<overlay_element> reload_state = std::make_unique<home_menu_entry>(
get_localized_string(localized_string_id::HOME_MENU_RELOAD_SAVESTATE));
add_item(reload_state, [](pad_button btn) -> page_navigation
{ {
if (btn != pad_button::cross) return page_navigation::stay; if (boot_current_game_savestate(true, save_index))
rsx_log.notice("User selected reload savestate in home menu"); {
Emu.CallFromMainThread([]() { boot_current_game_savestate(true, 1); }); const localized_string_id str_id = static_cast<localized_string_id>(static_cast<usz>(localized_string_id::HOME_MENU_RELOAD_SAVESTATE) + (save_index - 1));
std::unique_ptr<overlay_element> reload_state = std::make_unique<home_menu_entry>(get_localized_string(str_id));
add_item(reload_state, [save_index](pad_button btn) -> page_navigation
{
if (btn != pad_button::cross)
{
return page_navigation::stay;
}
rsx_log.notice("User selected reload savestate(%u) in home menu", save_index);
Emu.CallFromMainThread([save_index]() { boot_current_game_savestate(false, save_index); });
return page_navigation::exit; return page_navigation::exit;
}); });
} }
else
{
break;
}
}
apply_layout(); apply_layout();
} }
} }

View file

@ -529,7 +529,7 @@ void initialize()
// WPOS // WPOS
vr0 = vec4(abs(wpos_scale), wpos_scale, 1., 1.); vr0 = vec4(abs(wpos_scale), wpos_scale, 1., 1.);
vr1 = vec4(0., wpos_bias, 0., 0.); vr1 = vec4(wpos_bias, 0., 0.);
wpos = gl_FragCoord * vr0 + vr1; wpos = gl_FragCoord * vr0 + vr1;
// Other // Other

View file

@ -43,10 +43,9 @@ struct fragment_context_t
float fog_param1; float fog_param1;
uint rop_control; uint rop_control;
float alpha_ref; float alpha_ref;
uint reserved;
uint fog_mode; uint fog_mode;
float wpos_scale; float wpos_scale;
float wpos_bias; vec2 wpos_bias;
}; };
)" )"

View file

@ -38,7 +38,7 @@ bool _fragment_discard = false;
vec4 get_wpos() vec4 get_wpos()
{ {
float abs_scale = abs(wpos_scale); float abs_scale = abs(wpos_scale);
return (gl_FragCoord * vec4(abs_scale, wpos_scale, 1., 1.)) + vec4(0., wpos_bias, 0., 0.); return (gl_FragCoord * vec4(abs_scale, wpos_scale, 1., 1.)) + vec4(wpos_bias, 0., 0.);
} }
#endif #endif

View file

@ -374,6 +374,7 @@ namespace rsx
// sync // sync
void sync(); void sync();
flags32_t read_barrier(u32 memory_address, u32 memory_range, bool unconditional); flags32_t read_barrier(u32 memory_address, u32 memory_range, bool unconditional);
virtual void write_barrier(u32 /*memory_address*/, u32 /*memory_range*/) {}
virtual void sync_hint(FIFO::interrupt_hint hint, reports::sync_hint_payload_t payload); virtual void sync_hint(FIFO::interrupt_hint hint, reports::sync_hint_payload_t payload);
virtual bool release_GCM_label(u32 /*address*/, u32 /*value*/) { return false; } virtual bool release_GCM_label(u32 /*address*/, u32 /*value*/) { return false; }

View file

@ -1613,6 +1613,12 @@ void VKGSRender::on_guest_texture_read(const vk::command_buffer& cmd)
vkCmdUpdateBuffer(cmd, m_host_object_data->value, ::offset32(&vk::host_data_t::texture_load_complete_event), sizeof(u64), &event_id); vkCmdUpdateBuffer(cmd, m_host_object_data->value, ::offset32(&vk::host_data_t::texture_load_complete_event), sizeof(u64), &event_id);
} }
void VKGSRender::write_barrier(u32 address, u32 range)
{
ensure(is_current_thread());
m_rtts.invalidate_range(utils::address_range32::start_length(address, range));
}
void VKGSRender::sync_hint(rsx::FIFO::interrupt_hint hint, rsx::reports::sync_hint_payload_t payload) void VKGSRender::sync_hint(rsx::FIFO::interrupt_hint hint, rsx::reports::sync_hint_payload_t payload)
{ {
rsx::thread::sync_hint(hint, payload); rsx::thread::sync_hint(hint, payload);

View file

@ -249,6 +249,8 @@ public:
void set_scissor(bool clip_viewport); void set_scissor(bool clip_viewport);
void bind_viewport(); void bind_viewport();
// Sync
void write_barrier(u32 address, u32 range) override;
void sync_hint(rsx::FIFO::interrupt_hint hint, rsx::reports::sync_hint_payload_t payload) override; void sync_hint(rsx::FIFO::interrupt_hint hint, rsx::reports::sync_hint_payload_t payload) override;
bool release_GCM_label(u32 address, u32 data) override; bool release_GCM_label(u32 address, u32 data) override;

View file

@ -501,8 +501,11 @@ namespace vk
if (auto ptr = std::get_if<descriptor_image_array_t>(&slot)) if (auto ptr = std::get_if<descriptor_image_array_t>(&slot))
{ {
// We need to convert the VkDescriptorImageInfoEx entries back to the native vulkan variants since we're going to be flushing an array with no stride check
auto vk_data = ptr->map(FN(static_cast<VkDescriptorImageInfo>(x)));
writer.descriptorCount = ptr->size(); writer.descriptorCount = ptr->size();
m_descriptor_set.push(ptr->data(), ptr->size(), type, idx); m_descriptor_set.push(vk_data.data(), vk_data.size(), type, idx);
return; return;
} }
@ -552,8 +555,9 @@ namespace vk
if (auto ptr = std::get_if<descriptor_image_array_t>(&slot)) if (auto ptr = std::get_if<descriptor_image_array_t>(&slot))
{ {
auto vk_data = ptr->map(FN(static_cast<VkDescriptorImageInfo>(x))); // This can be optimized to update only changed ids but this is an interpreter-only feature for now
ensure(m_descriptor_template[idx].descriptorCount == ptr->size()); ensure(m_descriptor_template[idx].descriptorCount == ptr->size());
m_descriptor_template[idx].pImageInfo = m_descriptor_set.store(*ptr); m_descriptor_template[idx].pImageInfo = m_descriptor_set.store(vk_data);
return; return;
} }

View file

@ -403,6 +403,8 @@ namespace vk
const auto dst_aspect = dst->aspect(); const auto dst_aspect = dst->aspect();
const auto dst_bpp = vk::get_format_texel_width(dst->format()); const auto dst_bpp = vk::get_format_texel_width(dst->format());
std::unordered_set<decltype(sections_to_transfer.front().src)> processed_input_images;
for (const auto& section : sections_to_transfer) for (const auto& section : sections_to_transfer)
{ {
if (!section.src) if (!section.src)
@ -436,11 +438,15 @@ namespace vk
const bool typeless = section.src->aspect() != dst_aspect || const bool typeless = section.src->aspect() != dst_aspect ||
!formats_are_bitcast_compatible(dst, section.src); !formats_are_bitcast_compatible(dst, section.src);
if (!processed_input_images.contains(section.src))
{
// Avoid inserting unnecessary barrier GENERAL->TRANSFER_SRC->GENERAL in active render targets // Avoid inserting unnecessary barrier GENERAL->TRANSFER_SRC->GENERAL in active render targets
const auto preferred_layout = (section.src->current_layout != VK_IMAGE_LAYOUT_GENERAL) ? const auto preferred_layout = (section.src->current_layout != VK_IMAGE_LAYOUT_GENERAL) ?
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL; VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL;
section.src->push_layout(cmd, preferred_layout); section.src->push_layout(cmd, preferred_layout);
processed_input_images.insert(section.src);
}
auto src_image = section.src; auto src_image = section.src;
auto src_x = section.src_x; auto src_x = section.src_x;
@ -479,8 +485,6 @@ namespace vk
const areai src_rect = coordi{{ src_x, src_y }, { src_w, src_h }}; const areai src_rect = coordi{{ src_x, src_y }, { src_w, src_h }};
const areai dst_rect = coordi{{ section.dst_x, section.dst_y }, { section.dst_w, section.dst_h }}; const areai dst_rect = coordi{{ section.dst_x, section.dst_y }, { section.dst_w, section.dst_h }};
vk::copy_image_typeless(cmd, section.src, dst, src_rect, dst_rect, 1); vk::copy_image_typeless(cmd, section.src, dst, src_rect, dst_rect, 1);
section.src->pop_layout(cmd);
continue; continue;
} }
@ -541,8 +545,12 @@ namespace vk
vkCmdCopyImage(cmd, _dst->value, _dst->current_layout, dst->value, dst->current_layout, 1, &copy_rgn); vkCmdCopyImage(cmd, _dst->value, _dst->current_layout, dst->value, dst->current_layout, 1, &copy_rgn);
} }
} }
}
section.src->pop_layout(cmd); // Pop unique image layouts here
for (auto& image : processed_input_images)
{
image->pop_layout(cmd);
} }
} }

View file

@ -3132,7 +3132,7 @@ struct registers_decoder<NV4097_SET_SHADER_WINDOW>
return to_window_origin(window_shader_origin_raw()); return to_window_origin(window_shader_origin_raw());
} }
auto window_shader_pixel_center() const auto pixel_center() const
{ {
return to_window_pixel_center(window_shader_pixel_center_raw()); return to_window_pixel_center(window_shader_pixel_center_raw());
} }
@ -3146,7 +3146,7 @@ struct registers_decoder<NV4097_SET_SHADER_WINDOW>
static void dump(std::string& out, const decoded_type& decoded) static void dump(std::string& out, const decoded_type& decoded)
{ {
fmt::append(out, "Viewport: height: %u origin: %s pixel center: %s", decoded.window_shader_height() fmt::append(out, "Viewport: height: %u origin: %s pixel center: %s", decoded.window_shader_height()
, decoded.window_shader_origin(), decoded.window_shader_pixel_center()); , decoded.window_shader_origin(), decoded.pixel_center());
} }
}; };

View file

@ -226,9 +226,9 @@ namespace rsx
return decode<NV4097_SET_SHADER_WINDOW>().window_shader_origin(); return decode<NV4097_SET_SHADER_WINDOW>().window_shader_origin();
} }
window_pixel_center shader_window_pixel() const window_pixel_center pixel_center() const
{ {
return decode<NV4097_SET_SHADER_WINDOW>().window_shader_pixel_center(); return decode<NV4097_SET_SHADER_WINDOW>().pixel_center();
} }
u16 shader_window_height() const u16 shader_window_height() const
@ -1321,6 +1321,16 @@ namespace rsx
{ {
return decode<NV4097_SET_POLYGON_STIPPLE>().enabled(); return decode<NV4097_SET_POLYGON_STIPPLE>().enabled();
} }
polygon_mode polygon_mode_front() const
{
return decode<NV4097_SET_FRONT_POLYGON_MODE>().front_polygon_mode();
}
polygon_mode polygon_mode_back() const
{
return decode<NV4097_SET_BACK_POLYGON_MODE>().back_polygon_mode();
}
}; };
extern rsx_state method_registers; extern rsx_state method_registers;

View file

@ -2083,30 +2083,6 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
} }
} }
// Check game updates
if (const std::string hdd0_boot = hdd0_game + m_title_id + "/USRDIR/EBOOT.BIN"; !m_ar
&& recursion_count == 0 && disc.empty() && !bdvd_dir.empty() && !m_title_id.empty()
&& resolved_path == GetCallbacks().resolve_path(vfs::get("/dev_bdvd/PS3_GAME/USRDIR/EBOOT.BIN"))
&& resolved_path != GetCallbacks().resolve_path(hdd0_boot) && fs::is_file(hdd0_boot))
{
if (const psf::registry update_sfo = psf::load(hdd0_game + m_title_id + "/PARAM.SFO").sfo;
psf::get_string(update_sfo, "TITLE_ID") == m_title_id && psf::get_string(update_sfo, "CATEGORY") == "GD")
{
// Booting game update
sys_log.success("Updates found at /dev_hdd0/game/%s/", m_title_id);
m_path = hdd0_boot;
const game_boot_result boot_result = Load(m_title_id, true, recursion_count + 1);
if (boot_result == game_boot_result::no_errors)
{
return game_boot_result::no_errors;
}
sys_log.error("Failed to boot update at \"%s\", game update may be corrupted! Consider uninstalling or reinstalling it. (reason: %s)", m_path, boot_result);
return boot_result;
}
}
// Check firmware version // Check firmware version
if (const std::string_view game_fw_version = psf::get_string(_psf, "PS3_SYSTEM_VER", ""); !game_fw_version.empty()) if (const std::string_view game_fw_version = psf::get_string(_psf, "PS3_SYSTEM_VER", ""); !game_fw_version.empty())
{ {
@ -2144,12 +2120,6 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
std::replace(m_title.begin(), m_title.end(), '\n', ' '); std::replace(m_title.begin(), m_title.end(), '\n', ' ');
std::replace(m_localized_title.begin(), m_localized_title.end(), '\n', ' '); std::replace(m_localized_title.begin(), m_localized_title.end(), '\n', ' ');
// Mount /host_root/ if necessary (special value)
if (g_cfg.vfs.host_root)
{
vfs::mount("/host_root", "/");
}
// Open SELF or ELF // Open SELF or ELF
std::string elf_path = m_path; std::string elf_path = m_path;
@ -2214,9 +2184,40 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
return game_boot_result::decryption_error; return game_boot_result::decryption_error;
} }
// Check EBOOT.BIN (before updates - disc games)
ppu_exec_object ppu_exec;
ppu_exec.open(elf_file);
// Check game updates
if (const std::string hdd0_boot = hdd0_game + m_title_id + "/USRDIR/EBOOT.BIN"; !m_ar
&& recursion_count == 0 && disc.empty() && !bdvd_dir.empty() && !m_title_id.empty()
&& resolved_path == GetCallbacks().resolve_path(vfs::get("/dev_bdvd/PS3_GAME/USRDIR/EBOOT.BIN"))
&& resolved_path != GetCallbacks().resolve_path(hdd0_boot) && fs::is_file(hdd0_boot)
&& ppu_exec == elf_error::ok)
{
if (const psf::registry update_sfo = psf::load(hdd0_game + m_title_id + "/PARAM.SFO").sfo;
psf::get_string(update_sfo, "TITLE_ID") == m_title_id && psf::get_string(update_sfo, "CATEGORY") == "GD")
{
ppu_exec = {};
elf_file.close();
// Booting game update
sys_log.success("Updates found at /dev_hdd0/game/%s/", m_title_id);
m_path = hdd0_boot;
const game_boot_result boot_result = Load(m_title_id, true, recursion_count + 1);
if (boot_result == game_boot_result::no_errors)
{
return game_boot_result::no_errors;
}
sys_log.error("Failed to boot update at \"%s\", game update may be corrupted! Consider uninstalling or reinstalling it. (reason: %s)", m_path, boot_result);
return boot_result;
}
}
m_state = system_state::ready; m_state = system_state::ready;
ppu_exec_object ppu_exec;
ppu_prx_object ppu_prx; ppu_prx_object ppu_prx;
ppu_rel_object ppu_rel; ppu_rel_object ppu_rel;
spu_exec_object spu_exec; spu_exec_object spu_exec;
@ -2224,19 +2225,25 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
vm::init(); vm::init();
if (ppu_exec == elf_error::ok)
{
if (m_ar) if (m_ar)
{ {
vm::load(*m_ar); vm::load(*m_ar);
} }
// Mount /host_root/ if necessary (special value)
if (g_cfg.vfs.host_root)
{
vfs::mount("/host_root", "/");
}
if (!hdd1.empty()) if (!hdd1.empty())
{ {
vfs::mount("/dev_hdd1", hdd1); vfs::mount("/dev_hdd1", hdd1);
sys_log.notice("Hdd1: %s", vfs::get("/dev_hdd1")); sys_log.notice("Hdd1: %s", vfs::get("/dev_hdd1"));
} }
if (ppu_exec.open(elf_file) == elf_error::ok)
{
// PS3 executable // PS3 executable
GetCallbacks().on_ready(); GetCallbacks().on_ready();
@ -2384,6 +2391,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
else if (ppu_prx.open(elf_file) == elf_error::ok) else if (ppu_prx.open(elf_file) == elf_error::ok)
{ {
// PPU PRX // PPU PRX
m_ar.reset();
GetCallbacks().on_ready(); GetCallbacks().on_ready();
g_fxo->init(false); g_fxo->init(false);
ppu_load_prx(ppu_prx, false, m_path); ppu_load_prx(ppu_prx, false, m_path);
@ -2392,6 +2400,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
else if (spu_exec.open(elf_file) == elf_error::ok) else if (spu_exec.open(elf_file) == elf_error::ok)
{ {
// SPU executable // SPU executable
m_ar.reset();
GetCallbacks().on_ready(); GetCallbacks().on_ready();
g_fxo->init(false); g_fxo->init(false);
spu_load_exec(spu_exec); spu_load_exec(spu_exec);
@ -2400,6 +2409,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
else if (spu_rel.open(elf_file) == elf_error::ok) else if (spu_rel.open(elf_file) == elf_error::ok)
{ {
// SPU linker file // SPU linker file
m_ar.reset();
GetCallbacks().on_ready(); GetCallbacks().on_ready();
g_fxo->init(false); g_fxo->init(false);
spu_load_rel_exec(spu_rel); spu_load_rel_exec(spu_rel);
@ -2408,6 +2418,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
else if (ppu_rel.open(elf_file) == elf_error::ok) else if (ppu_rel.open(elf_file) == elf_error::ok)
{ {
// PPU linker file // PPU linker file
m_ar.reset();
GetCallbacks().on_ready(); GetCallbacks().on_ready();
g_fxo->init(false); g_fxo->init(false);
ppu_load_rel_exec(ppu_rel); ppu_load_rel_exec(ppu_rel);

View file

@ -274,6 +274,9 @@ enum class localized_string_id
HOME_MENU_SAVESTATE_SAVE, HOME_MENU_SAVESTATE_SAVE,
HOME_MENU_SAVESTATE_AND_EXIT, HOME_MENU_SAVESTATE_AND_EXIT,
HOME_MENU_RELOAD_SAVESTATE, HOME_MENU_RELOAD_SAVESTATE,
HOME_MENU_RELOAD_SECOND_SAVESTATE,
HOME_MENU_RELOAD_THIRD_SAVESTATE,
HOME_MENU_RELOAD_FOURTH_SAVESTATE,
HOME_MENU_RECORDING, HOME_MENU_RECORDING,
HOME_MENU_TROPHIES, HOME_MENU_TROPHIES,
HOME_MENU_TROPHY_LIST_TITLE, HOME_MENU_TROPHY_LIST_TITLE,

View file

@ -189,8 +189,6 @@ void ds4_pad_handler::init_config(cfg_pad* cfg)
cfg->rstickdeadzone.def = 40; // between 0 and 255 cfg->rstickdeadzone.def = 40; // between 0 and 255
cfg->ltriggerthreshold.def = 0; // between 0 and 255 cfg->ltriggerthreshold.def = 0; // between 0 and 255
cfg->rtriggerthreshold.def = 0; // between 0 and 255 cfg->rtriggerthreshold.def = 0; // between 0 and 255
cfg->lpadsquircling.def = 8000;
cfg->rpadsquircling.def = 8000;
// Set default color value // Set default color value
cfg->colorR.def = 0; cfg->colorR.def = 0;

View file

@ -262,8 +262,6 @@ void dualsense_pad_handler::init_config(cfg_pad* cfg)
cfg->rstickdeadzone.def = 40; // between 0 and 255 cfg->rstickdeadzone.def = 40; // between 0 and 255
cfg->ltriggerthreshold.def = 0; // between 0 and 255 cfg->ltriggerthreshold.def = 0; // between 0 and 255
cfg->rtriggerthreshold.def = 0; // between 0 and 255 cfg->rtriggerthreshold.def = 0; // between 0 and 255
cfg->lpadsquircling.def = 8000;
cfg->rpadsquircling.def = 8000;
// Set default color value // Set default color value
cfg->colorR.def = 0; cfg->colorR.def = 0;

View file

@ -120,8 +120,6 @@ void evdev_joystick_handler::init_config(cfg_pad* cfg)
cfg->rstickdeadzone.def = 30; // between 0 and 255 cfg->rstickdeadzone.def = 30; // between 0 and 255
cfg->ltriggerthreshold.def = 0; // between 0 and 255 cfg->ltriggerthreshold.def = 0; // between 0 and 255
cfg->rtriggerthreshold.def = 0; // between 0 and 255 cfg->rtriggerthreshold.def = 0; // between 0 and 255
cfg->lpadsquircling.def = 8000;
cfg->rpadsquircling.def = 8000;
// apply defaults // apply defaults
cfg->from_default(); cfg->from_default();

View file

@ -169,7 +169,7 @@ bool gui_pad_thread::init()
usetup.id.bustype = BUS_USB; usetup.id.bustype = BUS_USB;
usetup.id.vendor = 0x1234; usetup.id.vendor = 0x1234;
usetup.id.product = 0x1234; usetup.id.product = 0x1234;
std::strcpy(usetup.name, "RPCS3 GUI Input Device"); strcpy_trunc(usetup.name, "RPCS3 GUI Input Device"sv);
// The ioctls below will enable the device that is about to be created to pass events. // The ioctls below will enable the device that is about to be created to pass events.
CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_EVBIT, EV_KEY)); CHECK_IOCTRL_RET(ioctl(m_uinput_fd, UI_SET_EVBIT, EV_KEY));

View file

@ -68,8 +68,6 @@ void keyboard_pad_handler::init_config(cfg_pad* cfg)
cfg->rstickdeadzone.def = 0; cfg->rstickdeadzone.def = 0;
cfg->ltriggerthreshold.def = 0; cfg->ltriggerthreshold.def = 0;
cfg->rtriggerthreshold.def = 0; cfg->rtriggerthreshold.def = 0;
cfg->lpadsquircling.def = 8000;
cfg->rpadsquircling.def = 8000;
// apply defaults // apply defaults
cfg->from_default(); cfg->from_default();

View file

@ -62,8 +62,6 @@ void mm_joystick_handler::init_config(cfg_pad* cfg)
cfg->rstickdeadzone.def = 0; // between 0 and 255 cfg->rstickdeadzone.def = 0; // between 0 and 255
cfg->ltriggerthreshold.def = 0; // between 0 and 255 cfg->ltriggerthreshold.def = 0; // between 0 and 255
cfg->rtriggerthreshold.def = 0; // between 0 and 255 cfg->rtriggerthreshold.def = 0; // between 0 and 255
cfg->lpadsquircling.def = 8000;
cfg->rpadsquircling.def = 8000;
// apply defaults // apply defaults
cfg->from_default(); cfg->from_default();

View file

@ -158,8 +158,6 @@ void sdl_pad_handler::init_config(cfg_pad* cfg)
cfg->rstickdeadzone.def = 8000; // between 0 and SDL_JOYSTICK_AXIS_MAX cfg->rstickdeadzone.def = 8000; // between 0 and SDL_JOYSTICK_AXIS_MAX
cfg->ltriggerthreshold.def = 0; // between 0 and SDL_JOYSTICK_AXIS_MAX cfg->ltriggerthreshold.def = 0; // between 0 and SDL_JOYSTICK_AXIS_MAX
cfg->rtriggerthreshold.def = 0; // between 0 and SDL_JOYSTICK_AXIS_MAX cfg->rtriggerthreshold.def = 0; // between 0 and SDL_JOYSTICK_AXIS_MAX
cfg->lpadsquircling.def = 8000;
cfg->rpadsquircling.def = 8000;
// Set default color value // Set default color value
cfg->colorR.def = 0; cfg->colorR.def = 0;

View file

@ -128,8 +128,6 @@ void xinput_pad_handler::init_config(cfg_pad* cfg)
cfg->rstickdeadzone.def = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; // between 0 and 32767 cfg->rstickdeadzone.def = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE; // between 0 and 32767
cfg->ltriggerthreshold.def = XINPUT_GAMEPAD_TRIGGER_THRESHOLD; // between 0 and 255 cfg->ltriggerthreshold.def = XINPUT_GAMEPAD_TRIGGER_THRESHOLD; // between 0 and 255
cfg->rtriggerthreshold.def = XINPUT_GAMEPAD_TRIGGER_THRESHOLD; // between 0 and 255 cfg->rtriggerthreshold.def = XINPUT_GAMEPAD_TRIGGER_THRESHOLD; // between 0 and 255
cfg->lpadsquircling.def = 8000;
cfg->rpadsquircling.def = 8000;
// apply defaults // apply defaults
cfg->from_default(); cfg->from_default();

View file

@ -253,7 +253,10 @@ namespace psf
if (indices[i].param_fmt == format::string) if (indices[i].param_fmt == format::string)
{ {
// Find null terminator // Find null terminator
value.resize(std::strlen(value.c_str())); if (usz nts = value.find_first_of('\0'); nts != umax)
{
value.resize(nts);
}
} }
result.sfo.emplace(std::piecewise_construct, result.sfo.emplace(std::piecewise_construct,

View file

@ -2,6 +2,7 @@
#include "Emu/VFS.h" #include "Emu/VFS.h"
#include "Emu/System.h" #include "Emu/System.h"
#include "Emu/Cell/timers.hpp"
#include "Crypto/unself.h" #include "Crypto/unself.h"
@ -13,6 +14,7 @@
#include <charconv> #include <charconv>
#include <span> #include <span>
#include <thread>
LOG_CHANNEL(tar_log, "TAR"); LOG_CHANNEL(tar_log, "TAR");
@ -200,8 +202,7 @@ std::unique_ptr<utils::serial> tar_object::get_file(const std::string& path, std
bool tar_object::extract(const std::string& prefix_path, bool is_vfs) bool tar_object::extract(const std::string& prefix_path, bool is_vfs)
{ {
std::vector<u8> filedata_buffer(0x80'0000); std::vector<std::vector<u8>> filedata_buffers;
std::span<u8> filedata_span{filedata_buffer.data(), filedata_buffer.size()};
auto iter = m_map.begin(); auto iter = m_map.begin();
@ -294,6 +295,13 @@ bool tar_object::extract(const std::string& prefix_path, bool is_vfs)
fs::file file; fs::file file;
const u64 current_time = get_system_time();
const usz filesize = file_data->get_size() - file_data->pos;
constexpr usz chunk_size = 0x8 * 0x100000;
constexpr usz chunk_count = 16;
if (should_ignore) if (should_ignore)
{ {
file = fs::make_stream<std::vector<u8>>(); file = fs::make_stream<std::vector<u8>>();
@ -301,34 +309,89 @@ bool tar_object::extract(const std::string& prefix_path, bool is_vfs)
else else
{ {
file.open(result, fs::rewrite); file.open(result, fs::rewrite);
filedata_buffers.clear();
for (usz i = 0; i < std::min<usz>(utils::aligned_div<usz>(filesize, chunk_size), chunk_count); i++)
{
if (filedata_buffers.size() <= i)
{
filedata_buffers.resize(i + 1);
}
filedata_buffers[i].resize(std::min<usz>(filesize - i * chunk_size, chunk_size));
}
} }
if (file && file_data) if (file && file_data)
{
std::unique_ptr<named_thread<std::function<void()>>> async_reader;
atomic_t<usz> filedata_read_pos = 0, filedata_write_pos = 0;
while (!should_ignore && filesize)
{
auto get_span_at = [&](usz pos)
{
auto& span = filedata_buffers[pos % filedata_buffers.size()];
return std::span<u8>(span.data(), std::min<usz>(filesize - pos * chunk_size, chunk_size));
};
// Feed itself if smaller than one chunk
if (filedata_buffers.size() == 1)
{
file_data->try_read(get_span_at(filedata_read_pos));
filedata_read_pos++;
}
else if (!async_reader)
{
async_reader = std::make_unique<named_thread<std::function<void()>>>("TAR Extract File Thread", [&]()
{ {
while (true) while (true)
{ {
const usz unread_size = file_data->try_read(filedata_span); while (filedata_read_pos - filedata_write_pos == filedata_buffers.size())
if (unread_size == 0)
{ {
file.write(filedata_span.data(), should_ignore ? 0 : filedata_span.size()); thread_ctrl::wait_for(1000);
continue;
} }
// Tail data const usz unread_size = file_data->try_read(get_span_at(filedata_read_pos));
if (usz read_size = filedata_span.size() - unread_size) if (unread_size)
{ {
ensure(file_data->try_read(filedata_span.first(read_size)) == 0); ensure(unread_size == filedata_buffers[filedata_read_pos.load() % filedata_buffers.size()].size());
file.write(filedata_span.data(), should_ignore ? 0 : read_size); break;
}
filedata_read_pos++;
}
});
}
while (filedata_read_pos == filedata_write_pos)
{
std::this_thread::yield();
}
const auto data_span = get_span_at(filedata_write_pos);
file.write(data_span.data(), data_span.size());
filedata_write_pos++;
if (filedata_write_pos == utils::aligned_div<usz>(filesize, filedata_buffers[0].size()))
{
if (async_reader)
{
// Join thread
(*async_reader)();
async_reader.reset();
} }
break; break;
} }
}
file.close(); file.close();
file_data->seek_pos(m_ar_tar_start + largest_offset, true); file_data->seek_pos(m_ar_tar_start + largest_offset, true);
if (!m_file) if (!m_file)
@ -349,7 +412,7 @@ bool tar_object::extract(const std::string& prefix_path, bool is_vfs)
return false; return false;
} }
tar_log.notice("TAR Loader: written file %s", name); (m_ar && filesize > 1024 ? tar_log.success : tar_log.notice)("TAR Loader: written file %s (took: %f seconds)", name, (get_system_time() - current_time) / 1'000'000.);
break; break;
} }

View file

@ -450,6 +450,7 @@
<ClCompile Include="Emu\Io\Dimensions.cpp" /> <ClCompile Include="Emu\Io\Dimensions.cpp" />
<ClCompile Include="Emu\Io\Infinity.cpp" /> <ClCompile Include="Emu\Io\Infinity.cpp" />
<ClCompile Include="Emu\Io\Skylander.cpp" /> <ClCompile Include="Emu\Io\Skylander.cpp" />
<ClCompile Include="Emu\Io\KamenRider.cpp" />
<ClCompile Include="Emu\Io\usb_device.cpp" /> <ClCompile Include="Emu\Io\usb_device.cpp" />
<ClCompile Include="Emu\Io\usb_vfs.cpp" /> <ClCompile Include="Emu\Io\usb_vfs.cpp" />
<ClCompile Include="Emu\RSX\Capture\rsx_capture.cpp" /> <ClCompile Include="Emu\RSX\Capture\rsx_capture.cpp" />
@ -545,6 +546,7 @@
<ClInclude Include="..\3rdparty\stblib\stb\stb_image.h" /> <ClInclude Include="..\3rdparty\stblib\stb\stb_image.h" />
<ClInclude Include="..\Utilities\address_range.h" /> <ClInclude Include="..\Utilities\address_range.h" />
<ClInclude Include="..\Utilities\cheat_info.h" /> <ClInclude Include="..\Utilities\cheat_info.h" />
<ClInclude Include="..\Utilities\deferred_op.hpp" />
<ClInclude Include="..\Utilities\simple_ringbuf.h" /> <ClInclude Include="..\Utilities\simple_ringbuf.h" />
<ClInclude Include="..\Utilities\stack_trace.h" /> <ClInclude Include="..\Utilities\stack_trace.h" />
<ClInclude Include="..\Utilities\transactional_storage.h" /> <ClInclude Include="..\Utilities\transactional_storage.h" />
@ -809,6 +811,7 @@
<ClInclude Include="Emu\Io\Dimensions.h" /> <ClInclude Include="Emu\Io\Dimensions.h" />
<ClInclude Include="Emu\Io\Infinity.h" /> <ClInclude Include="Emu\Io\Infinity.h" />
<ClInclude Include="Emu\Io\Skylander.h" /> <ClInclude Include="Emu\Io\Skylander.h" />
<ClInclude Include="Emu\Io\KamenRider.h" />
<ClInclude Include="Emu\Io\usb_device.h" /> <ClInclude Include="Emu\Io\usb_device.h" />
<ClInclude Include="Emu\Io\usb_vfs.h" /> <ClInclude Include="Emu\Io\usb_vfs.h" />
<ClInclude Include="Emu\IPC.h" /> <ClInclude Include="Emu\IPC.h" />

View file

@ -879,6 +879,9 @@
<ClCompile Include="Emu\Io\Skylander.cpp"> <ClCompile Include="Emu\Io\Skylander.cpp">
<Filter>Emu\Io</Filter> <Filter>Emu\Io</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Emu\Io\KamenRider.cpp">
<Filter>Emu\Io</Filter>
</ClCompile>
<ClCompile Include="Emu\Cell\lv2\sys_overlay.cpp"> <ClCompile Include="Emu\Cell\lv2\sys_overlay.cpp">
<Filter>Emu\Cell\lv2</Filter> <Filter>Emu\Cell\lv2</Filter>
</ClCompile> </ClCompile>
@ -2079,6 +2082,9 @@
<ClInclude Include="Emu\Io\Skylander.h"> <ClInclude Include="Emu\Io\Skylander.h">
<Filter>Emu\Io</Filter> <Filter>Emu\Io</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Emu\Io\KamenRider.h">
<Filter>Emu\Io</Filter>
</ClInclude>
<ClInclude Include="Crypto\md5.h"> <ClInclude Include="Crypto\md5.h">
<Filter>Crypto</Filter> <Filter>Crypto</Filter>
</ClInclude> </ClInclude>
@ -2749,6 +2755,9 @@
<ClInclude Include="Emu\RSX\Common\reverse_ptr.hpp"> <ClInclude Include="Emu\RSX\Common\reverse_ptr.hpp">
<Filter>Emu\GPU\RSX\Common</Filter> <Filter>Emu\GPU\RSX\Common</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\Utilities\deferred_op.hpp">
<Filter>Utilities</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Emu\RSX\Program\GLSLSnippets\GPUDeswizzle.glsl"> <None Include="Emu\RSX\Program\GLSLSnippets\GPUDeswizzle.glsl">

View file

@ -328,6 +328,9 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_ipc_settings_dialog.cpp"> <ClCompile Include="QTGeneratedFiles\Debug\moc_ipc_settings_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_kamen_rider_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_kernel_explorer.cpp"> <ClCompile Include="QTGeneratedFiles\Debug\moc_kernel_explorer.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
@ -616,6 +619,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_ipc_settings_dialog.cpp"> <ClCompile Include="QTGeneratedFiles\Release\moc_ipc_settings_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_kamen_rider_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_kernel_explorer.cpp"> <ClCompile Include="QTGeneratedFiles\Release\moc_kernel_explorer.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
@ -819,6 +825,7 @@
<ClCompile Include="rpcs3qt\infinity_dialog.cpp" /> <ClCompile Include="rpcs3qt\infinity_dialog.cpp" />
<ClCompile Include="rpcs3qt\input_dialog.cpp" /> <ClCompile Include="rpcs3qt\input_dialog.cpp" />
<ClCompile Include="rpcs3qt\ipc_settings_dialog.cpp" /> <ClCompile Include="rpcs3qt\ipc_settings_dialog.cpp" />
<ClCompile Include="rpcs3qt\kamen_rider_dialog.cpp" />
<ClCompile Include="rpcs3qt\localized.cpp" /> <ClCompile Include="rpcs3qt\localized.cpp" />
<ClCompile Include="rpcs3qt\localized_emu.cpp" /> <ClCompile Include="rpcs3qt\localized_emu.cpp" />
<ClCompile Include="rpcs3qt\log_viewer.cpp" /> <ClCompile Include="rpcs3qt\log_viewer.cpp" />
@ -1665,6 +1672,16 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs> <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command> <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
</CustomBuild> </CustomBuild>
<CustomBuild Include="rpcs3qt\kamen_rider_dialog.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
</CustomBuild>
<CustomBuild Include="rpcs3qt\update_manager.h"> <CustomBuild Include="rpcs3qt\update_manager.h">
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message> <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs> <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>

View file

@ -107,6 +107,9 @@
<Filter Include="Gui\infinity"> <Filter Include="Gui\infinity">
<UniqueIdentifier>{f5fcca0d-918b-46ba-bb91-2f2f9d9ddbba}</UniqueIdentifier> <UniqueIdentifier>{f5fcca0d-918b-46ba-bb91-2f2f9d9ddbba}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Gui\kamen_rider">
<UniqueIdentifier>{8b4d2dff-2b4e-4794-9859-4379ef0e75c0}</UniqueIdentifier>
</Filter>
<Filter Include="Gui\skylanders"> <Filter Include="Gui\skylanders">
<UniqueIdentifier>{c25f8f80-cc74-4760-8488-a291b3026b1d}</UniqueIdentifier> <UniqueIdentifier>{c25f8f80-cc74-4760-8488-a291b3026b1d}</UniqueIdentifier>
</Filter> </Filter>
@ -639,6 +642,9 @@
<ClCompile Include="rpcs3qt\infinity_dialog.cpp"> <ClCompile Include="rpcs3qt\infinity_dialog.cpp">
<Filter>Gui\infinity</Filter> <Filter>Gui\infinity</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="rpcs3qt\kamen_rider_dialog.cpp">
<Filter>Gui\infinity</Filter>
</ClCompile>
<ClCompile Include="rpcs3qt\skylander_dialog.cpp"> <ClCompile Include="rpcs3qt\skylander_dialog.cpp">
<Filter>Gui\skylanders</Filter> <Filter>Gui\skylanders</Filter>
</ClCompile> </ClCompile>
@ -753,6 +759,12 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_infinity_dialog.cpp"> <ClCompile Include="QTGeneratedFiles\Release\moc_infinity_dialog.cpp">
<Filter>Generated Files\Release</Filter> <Filter>Generated Files\Release</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_kamen_rider_dialog.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_kamen_rider_dialog.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_skylander_dialog.cpp"> <ClCompile Include="QTGeneratedFiles\Debug\moc_skylander_dialog.cpp">
<Filter>Generated Files\Debug</Filter> <Filter>Generated Files\Debug</Filter>
</ClCompile> </ClCompile>
@ -1621,6 +1633,9 @@
<CustomBuild Include="rpcs3qt\infinity_dialog.h"> <CustomBuild Include="rpcs3qt\infinity_dialog.h">
<Filter>Gui\infinity</Filter> <Filter>Gui\infinity</Filter>
</CustomBuild> </CustomBuild>
<CustomBuild Include="rpcs3qt\kamen_rider_dialog.h">
<Filter>Gui\kamen_rider</Filter>
</CustomBuild>
<CustomBuild Include="rpcs3qt\skylander_dialog.h"> <CustomBuild Include="rpcs3qt\skylander_dialog.h">
<Filter>Gui\skylanders</Filter> <Filter>Gui\skylanders</Filter>
</CustomBuild> </CustomBuild>

View file

@ -45,6 +45,7 @@ add_library(rpcs3_ui STATIC
input_dialog.cpp input_dialog.cpp
instruction_editor_dialog.cpp instruction_editor_dialog.cpp
ipc_settings_dialog.cpp ipc_settings_dialog.cpp
kamen_rider_dialog.cpp
kernel_explorer.cpp kernel_explorer.cpp
localized.cpp localized.cpp
localized_emu.cpp localized_emu.cpp

View file

@ -0,0 +1,422 @@
#include "stdafx.h"
#include "Utilities/File.h"
#include "kamen_rider_dialog.h"
#include "Emu/Io/KamenRider.h"
#include <random>
#include <QLabel>
#include <QGroupBox>
#include <QFileDialog>
#include <QVBoxLayout>
#include <QMessageBox>
#include <QComboBox>
#include <QPushButton>
#include <QStringList>
#include <QCompleter>
kamen_rider_dialog* kamen_rider_dialog::inst = nullptr;
std::array<std::optional<std::tuple<u8, u8, u8>>, UI_FIG_NUM> kamen_rider_dialog::figure_slots = {};
QString last_kamen_rider_path;
static const std::map<const std::pair<const u8, const u8>, const std::string> list_kamen_riders = {
{{0x10, 0x10}, "Kamen Rider Drive Wind"},
{{0x10, 0x20}, "Kamen Rider Drive Water"},
{{0x10, 0x30}, "Kamen Rider Drive Fire"},
{{0x10, 0x40}, "Kamen Rider Drive Light"},
{{0x10, 0x50}, "Kamen Rider Drive Dark"},
{{0x11, 0x10}, "Kamen Rider Gaim Wind"},
{{0x11, 0x20}, "Kamen Rider Gaim Water"},
{{0x12, 0x20}, "Kamen Rider Wizard Water"},
{{0x12, 0x30}, "Kamen Rider Wizard Fire"},
{{0x13, 0x40}, "Kamen Rider Fourze Light"},
{{0x14, 0x20}, "Kamen Rider 000 Water"},
{{0x15, 0x10}, "Kamen Rider Double Wind"},
{{0x16, 0x50}, "Kamen Rider Decade Dark"},
{{0x17, 0x50}, "Kamen Rider Kiva Dark"},
{{0x18, 0x40}, "Kamen Rider Den-O Light"},
{{0x19, 0x30}, "Kamen Rider Kabuto Fire"},
{{0x1A, 0x30}, "Kamen Rider Hibiki Fire"},
{{0x1B, 0x50}, "Kamen Rider Blade Dark"},
{{0x1C, 0x50}, "Kamen Rider Faiz Dark"},
{{0x1D, 0x10}, "Kamen Rider Ryuki Wind"},
{{0x1E, 0x20}, "Kamen Rider Agito Water"},
{{0x1F, 0x40}, "Kamen Rider Kuuga Light"},
{{0x20, 0x00}, "Type Wild"},
{{0x21, 0x00}, "Kamen Rider Zangetsu"},
{{0x22, 0x00}, "All Dragon"},
{{0x31, 0x00}, "Kachidoki Arms"},
};
static u32 kamen_rider_crc32(const std::array<u8, 16>& buffer)
{
static constexpr std::array<u32, 256> CRC32_TABLE{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535,
0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd,
0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d,
0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac,
0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab,
0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb,
0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea,
0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce,
0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409,
0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739,
0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268,
0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0,
0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8,
0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703,
0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7,
0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae,
0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6,
0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d,
0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5,
0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
// Kamen Rider figures calculate their CRC32 based on 12 bytes in the block of 16
u32 ret = 0;
for (u32 i = 0; i < 12; ++i)
{
const u8 index = u8(ret & 0xFF) ^ buffer[i];
ret = ((ret >> 8) ^ CRC32_TABLE[index]);
}
return ret;
}
kamen_rider_creator_dialog::kamen_rider_creator_dialog(QWidget* parent)
: QDialog(parent)
{
setWindowTitle(tr("Kamen Rider Creator"));
setObjectName("kamen_rider_creator");
setMinimumSize(QSize(500, 150));
QVBoxLayout* vbox_panel = new QVBoxLayout();
QComboBox* combo_figlist = new QComboBox();
QStringList filterlist;
for (const auto& [entry, figure_name] : list_kamen_riders)
{
const uint qvar = (entry.first << 8) | entry.second;
QString name = QString::fromStdString(figure_name);
combo_figlist->addItem(name, QVariant(qvar));
filterlist << std::move(name);
}
combo_figlist->addItem(tr("--Unknown--"), QVariant(0xFFFF));
combo_figlist->setEditable(true);
combo_figlist->setInsertPolicy(QComboBox::NoInsert);
combo_figlist->model()->sort(0, Qt::AscendingOrder);
QCompleter* co_compl = new QCompleter(filterlist, this);
co_compl->setCaseSensitivity(Qt::CaseInsensitive);
co_compl->setCompletionMode(QCompleter::PopupCompletion);
co_compl->setFilterMode(Qt::MatchContains);
combo_figlist->setCompleter(co_compl);
vbox_panel->addWidget(combo_figlist);
QFrame* line = new QFrame();
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
vbox_panel->addWidget(line);
QHBoxLayout* hbox_idvar = new QHBoxLayout();
QLabel* label_id = new QLabel(tr("ID:"));
QLabel* label_type = new QLabel(tr("Type:"));
QLineEdit* edit_id = new QLineEdit("0");
QLineEdit* edit_type = new QLineEdit("0");
QRegularExpressionValidator* rxv = new QRegularExpressionValidator(QRegularExpression("\\d*"), this);
edit_id->setValidator(rxv);
edit_type->setValidator(rxv);
hbox_idvar->addWidget(label_id);
hbox_idvar->addWidget(edit_id);
hbox_idvar->addWidget(label_type);
hbox_idvar->addWidget(edit_type);
vbox_panel->addLayout(hbox_idvar);
QHBoxLayout* hbox_buttons = new QHBoxLayout();
QPushButton* btn_create = new QPushButton(tr("Create"), this);
QPushButton* btn_cancel = new QPushButton(tr("Cancel"), this);
hbox_buttons->addStretch();
hbox_buttons->addWidget(btn_create);
hbox_buttons->addWidget(btn_cancel);
vbox_panel->addLayout(hbox_buttons);
setLayout(vbox_panel);
connect(combo_figlist, QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int index)
{
const u16 fig_info = combo_figlist->itemData(index).toUInt();
if (fig_info != 0xFFFF)
{
const u8 fig_id = fig_info >> 8;
const u8 fig_type = fig_info & 0xFF;
edit_id->setText(QString::number(fig_id));
edit_type->setText(QString::number(fig_type));
}
});
connect(btn_create, &QAbstractButton::clicked, this, [=, this]()
{
bool ok_id = false, ok_var = false;
const u8 fig_id = edit_id->text().toUShort(&ok_id);
if (!ok_id)
{
QMessageBox::warning(this, tr("Error converting value"), tr("ID entered is invalid!"), QMessageBox::Ok);
return;
}
const u8 fig_type = edit_type->text().toUShort(&ok_var);
if (!ok_var)
{
QMessageBox::warning(this, tr("Error converting value"), tr("Variant entered is invalid!"), QMessageBox::Ok);
return;
}
QString predef_name = last_kamen_rider_path;
const auto found_fig = list_kamen_riders.find(std::make_pair(fig_id, fig_type));
if (found_fig != list_kamen_riders.cend())
{
predef_name += QString::fromStdString(found_fig->second + ".bin");
}
else
{
predef_name += QString("Unknown(%1 %2).bin").arg(fig_id).arg(fig_type);
}
file_path = QFileDialog::getSaveFileName(this, tr("Create Kamen Rider File"), predef_name, tr("Kamen Rider Object (*.bin);;All Files (*)"));
if (file_path.isEmpty())
{
return;
}
fs::file fig_file(file_path.toStdString(), fs::read + fs::write + fs::create);
if (!fig_file)
{
QMessageBox::warning(this, tr("Failed to create kamen rider file!"), tr("Failed to create kamen rider file:\n%1").arg(file_path), QMessageBox::Ok);
return;
}
std::array<u8, 0x14 * 0x10> buf{};
buf[0] = 0x04;
buf[6] = 0x80;
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> dist(0, 255);
buf[1] = dist(mt);
buf[2] = dist(mt);
buf[3] = dist(mt);
buf[4] = dist(mt);
buf[5] = dist(mt);
buf[7] = 0x89;
buf[8] = 0x44;
buf[10] = 0xc2;
std::array<u8, 16> figure_data = {u8(dist(mt)), 0x03, 0x00, 0x00, 0x01, 0x0e, 0x0a, 0x0a, 0x10, fig_type, 0x01, fig_id};
write_to_ptr<le_t<u32>>(figure_data.data(), 0xC, kamen_rider_crc32(figure_data));
memcpy(&buf[16], figure_data.data(), figure_data.size());
fig_file.write(buf.data(), buf.size());
fig_file.close();
last_kamen_rider_path = QFileInfo(file_path).absolutePath() + "/";
accept();
});
connect(btn_cancel, &QAbstractButton::clicked, this, &QDialog::reject);
connect(co_compl, QOverload<const QString&>::of(&QCompleter::activated), [=](const QString& text)
{
combo_figlist->setCurrentText(text);
combo_figlist->setCurrentIndex(combo_figlist->findText(text));
});
}
QString kamen_rider_creator_dialog::get_file_path() const
{
return file_path;
}
kamen_rider_dialog::kamen_rider_dialog(QWidget* parent)
: QDialog(parent)
{
setWindowTitle(tr("Kamen Rider Manager"));
setObjectName("kamen_riders_manager");
setAttribute(Qt::WA_DeleteOnClose);
setMinimumSize(QSize(700, 200));
QVBoxLayout* vbox_panel = new QVBoxLayout();
auto add_line = [](QVBoxLayout* vbox)
{
QFrame* line = new QFrame();
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
vbox->addWidget(line);
};
QGroupBox* group_kamen_riders = new QGroupBox(tr("Active Kamen Riders:"));
QVBoxLayout* vbox_group = new QVBoxLayout();
for (auto i = 0; i < UI_FIG_NUM; i++)
{
if (i != 0)
{
add_line(vbox_group);
}
QHBoxLayout* hbox_kamen_rider = new QHBoxLayout();
QLabel* label_figname = new QLabel(QString(tr("Kamen Rider %1")).arg(i + 1));
edit_kamen_riders[i] = new QLineEdit();
edit_kamen_riders[i]->setEnabled(false);
QPushButton* clear_btn = new QPushButton(tr("Clear"));
QPushButton* create_btn = new QPushButton(tr("Create"));
QPushButton* load_btn = new QPushButton(tr("Load"));
connect(clear_btn, &QAbstractButton::clicked, this, [this, i]()
{
clear_kamen_rider(i);
});
connect(create_btn, &QAbstractButton::clicked, this, [this, i]()
{
create_kamen_rider(i);
});
connect(load_btn, &QAbstractButton::clicked, this, [this, i]()
{
load_kamen_rider(i);
});
hbox_kamen_rider->addWidget(label_figname);
hbox_kamen_rider->addWidget(edit_kamen_riders[i]);
hbox_kamen_rider->addWidget(clear_btn);
hbox_kamen_rider->addWidget(create_btn);
hbox_kamen_rider->addWidget(load_btn);
vbox_group->addLayout(hbox_kamen_rider);
}
group_kamen_riders->setLayout(vbox_group);
vbox_panel->addWidget(group_kamen_riders);
setLayout(vbox_panel);
update_edits();
}
kamen_rider_dialog::~kamen_rider_dialog()
{
inst = nullptr;
}
kamen_rider_dialog* kamen_rider_dialog::get_dlg(QWidget* parent)
{
if (inst == nullptr)
inst = new kamen_rider_dialog(parent);
return inst;
}
void kamen_rider_dialog::clear_kamen_rider(u8 slot)
{
if (const auto& slot_infos = ::at32(figure_slots, slot))
{
const auto& [cur_slot, id, var] = slot_infos.value();
g_ridergate.remove_figure(cur_slot);
figure_slots[slot] = {};
update_edits();
}
}
void kamen_rider_dialog::create_kamen_rider(u8 slot)
{
kamen_rider_creator_dialog create_dlg(this);
if (create_dlg.exec() == Accepted)
{
load_kamen_rider_path(slot, create_dlg.get_file_path());
}
}
void kamen_rider_dialog::load_kamen_rider(u8 slot)
{
const QString file_path = QFileDialog::getOpenFileName(this, tr("Select Kamen Rider File"), last_kamen_rider_path, tr("Kamen Rider (*.bin);;All Files (*)"));
if (file_path.isEmpty())
{
return;
}
last_kamen_rider_path = QFileInfo(file_path).absolutePath() + "/";
load_kamen_rider_path(slot, file_path);
}
void kamen_rider_dialog::load_kamen_rider_path(u8 slot, const QString& path)
{
fs::file fig_file(path.toStdString(), fs::read + fs::write + fs::lock);
if (!fig_file)
{
QMessageBox::warning(this, tr("Failed to open the kamen rider file!"), tr("Failed to open the kamen rider file(%1)!\nFile may already be in use on the portal.").arg(path), QMessageBox::Ok);
return;
}
std::array<u8, 0x14 * 0x10> data;
if (fig_file.read(data.data(), data.size()) != data.size())
{
QMessageBox::warning(this, tr("Failed to read the kamen rider file!"), tr("Failed to read the kamen rider file(%1)!\nFile was too small.").arg(path), QMessageBox::Ok);
return;
}
clear_kamen_rider(slot);
u8 fig_id = data[0x1B];
u8 fig_type = data[0x19];
u8 portal_slot = g_ridergate.load_figure(data, std::move(fig_file));
figure_slots[slot] = std::tuple(portal_slot, fig_id, fig_type);
update_edits();
}
void kamen_rider_dialog::update_edits()
{
for (auto i = 0; i < UI_FIG_NUM; i++)
{
QString display_string;
if (const auto& sd = figure_slots[i])
{
const auto& [portal_slot, fig_id, fig_type] = sd.value();
const auto found_fig = list_kamen_riders.find(std::make_pair(fig_id, fig_type));
if (found_fig != list_kamen_riders.cend())
{
display_string = QString::fromStdString(found_fig->second);
}
else
{
display_string = QString(tr("Unknown (Id:%1 Var:%2)")).arg(fig_id).arg(fig_type);
}
}
else
{
display_string = tr("None");
}
edit_kamen_riders[i]->setText(display_string);
}
}

View file

@ -0,0 +1,49 @@
#pragma once
#include <optional>
#include "util/types.hpp"
#include <QDialog>
#include <QLineEdit>
constexpr auto UI_FIG_NUM = 8;
class kamen_rider_creator_dialog : public QDialog
{
Q_OBJECT
public:
explicit kamen_rider_creator_dialog(QWidget* parent);
QString get_file_path() const;
protected:
QString file_path;
};
class kamen_rider_dialog : public QDialog
{
Q_OBJECT
public:
explicit kamen_rider_dialog(QWidget* parent);
~kamen_rider_dialog();
static kamen_rider_dialog* get_dlg(QWidget* parent);
kamen_rider_dialog(kamen_rider_dialog const&) = delete;
void operator=(kamen_rider_dialog const&) = delete;
protected:
void clear_kamen_rider(u8 slot);
void create_kamen_rider(u8 slot);
void load_kamen_rider(u8 slot);
void load_kamen_rider_path(u8 slot, const QString& path);
void update_edits();
protected:
std::array<QLineEdit*, UI_FIG_NUM> edit_kamen_riders{};
static std::array<std::optional<std::tuple<u8, u8, u8>>, UI_FIG_NUM> figure_slots;
private:
static kamen_rider_dialog* inst;
};

View file

@ -295,6 +295,9 @@ private:
case localized_string_id::HOME_MENU_SAVESTATE_SAVE: return tr("Save Emulation State"); case localized_string_id::HOME_MENU_SAVESTATE_SAVE: return tr("Save Emulation State");
case localized_string_id::HOME_MENU_SAVESTATE_AND_EXIT: return tr("Save Emulation State And Exit"); case localized_string_id::HOME_MENU_SAVESTATE_AND_EXIT: return tr("Save Emulation State And Exit");
case localized_string_id::HOME_MENU_RELOAD_SAVESTATE: return tr("Reload Last Emulation State"); case localized_string_id::HOME_MENU_RELOAD_SAVESTATE: return tr("Reload Last Emulation State");
case localized_string_id::HOME_MENU_RELOAD_SECOND_SAVESTATE: return tr("Reload Second-To-Last Emulation State");
case localized_string_id::HOME_MENU_RELOAD_THIRD_SAVESTATE: return tr("Reload Third-To-Last Emulation State");
case localized_string_id::HOME_MENU_RELOAD_FOURTH_SAVESTATE: return tr("Reload Fourth-To-Last Emulation State");
case localized_string_id::HOME_MENU_RECORDING: return tr("Start/Stop Recording"); case localized_string_id::HOME_MENU_RECORDING: return tr("Start/Stop Recording");
case localized_string_id::HOME_MENU_TROPHIES: return tr("Trophies"); case localized_string_id::HOME_MENU_TROPHIES: return tr("Trophies");
case localized_string_id::HOME_MENU_TROPHY_LIST_TITLE: return tr("Trophy Progress: %0").arg(std::forward<Args>(args)...); case localized_string_id::HOME_MENU_TROPHY_LIST_TITLE: return tr("Trophy Progress: %0").arg(std::forward<Args>(args)...);

View file

@ -23,6 +23,7 @@
#include "skylander_dialog.h" #include "skylander_dialog.h"
#include "infinity_dialog.h" #include "infinity_dialog.h"
#include "dimensions_dialog.h" #include "dimensions_dialog.h"
#include "kamen_rider_dialog.h"
#include "cheat_manager.h" #include "cheat_manager.h"
#include "patch_manager_dialog.h" #include "patch_manager_dialog.h"
#include "patch_creator_dialog.h" #include "patch_creator_dialog.h"
@ -2999,6 +3000,12 @@ void main_window::CreateConnects()
dim_dlg->show(); dim_dlg->show();
}); });
connect(ui->actionManage_KamenRider_RideGate, &QAction::triggered, this, [this]
{
kamen_rider_dialog* kam_dlg = kamen_rider_dialog::get_dlg(this);
kam_dlg->show();
});
connect(ui->actionManage_Cheats, &QAction::triggered, this, [this] connect(ui->actionManage_Cheats, &QAction::triggered, this, [this]
{ {
cheat_manager_dialog* cheat_manager = cheat_manager_dialog::get_dlg(this); cheat_manager_dialog* cheat_manager = cheat_manager_dialog::get_dlg(this);

View file

@ -302,6 +302,7 @@
<addaction name="actionManage_Skylanders_Portal"/> <addaction name="actionManage_Skylanders_Portal"/>
<addaction name="actionManage_Infinity_Base"/> <addaction name="actionManage_Infinity_Base"/>
<addaction name="actionManage_Dimensions_ToyPad"/> <addaction name="actionManage_Dimensions_ToyPad"/>
<addaction name="actionManage_KamenRider_RideGate"/>
<addaction name="actionManage_Cheats"/> <addaction name="actionManage_Cheats"/>
<addaction name="actionManage_Game_Patches"/> <addaction name="actionManage_Game_Patches"/>
<addaction name="actionManage_Screenshots"/> <addaction name="actionManage_Screenshots"/>
@ -1173,6 +1174,11 @@
<string>Dimensions Toypad</string> <string>Dimensions Toypad</string>
</property> </property>
</action> </action>
<action name="actionManage_KamenRider_RideGate">
<property name="text">
<string>Kamen Rider Ride Gate</string>
</property>
</action>
<action name="actionManage_Cheats"> <action name="actionManage_Cheats">
<property name="text"> <property name="text">
<string>Cheats</string> <string>Cheats</string>

View file

@ -552,7 +552,7 @@ void savestate_manager_dialog::StartSavestateLoadThreads()
std::vector<std::unique_ptr<game_savestates_data>> game_data; std::vector<std::unique_ptr<game_savestates_data>> game_data;
qRegisterMetaType<QVector<int>>("QVector<int>"); qRegisterMetaType<QVector<int>>("QVector<int>");
QList<int> indices; QList<u64> indices;
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {
auto game_data_ptr = std::make_unique<game_savestates_data>(); auto game_data_ptr = std::make_unique<game_savestates_data>();
@ -595,7 +595,7 @@ void savestate_manager_dialog::StartSavestateLoadThreads()
}); });
atomic_t<usz> error_count{}; atomic_t<usz> error_count{};
future_watcher.setFuture(QtConcurrent::map(indices, [this, &error_count, &game_data](const int& i) future_watcher.setFuture(QtConcurrent::map(indices, [this, &error_count, &game_data](u64 i)
{ {
gui_log.trace("Loading savestate dir: %s", game_data[i]->title_id); gui_log.trace("Loading savestate dir: %s", game_data[i]->title_id);

View file

@ -164,9 +164,9 @@ static const std::map<const std::pair<const u16, const u16>, const std::string>
{{205, 0x0000}, "Sky Iron Shield"}, {{205, 0x0000}, "Sky Iron Shield"},
{{206, 0x0000}, "Winged Boots"}, {{206, 0x0000}, "Winged Boots"},
{{207, 0x0000}, "Sparx the Dragonfly"}, {{207, 0x0000}, "Sparx the Dragonfly"},
{{208, 0x0000}, "Dragonfire Cannon"}, {{208, 0x1206}, "Dragonfire Cannon"},
{{208, 0x1602}, "Golden Dragonfire Cannon"}, {{208, 0x1602}, "Golden Dragonfire Cannon"},
{{209, 0x0000}, "Scorpion Striker"}, {{209, 0x1206}, "Scorpion Striker"},
{{210, 0x3002}, "Biter's Bane"}, {{210, 0x3002}, "Biter's Bane"},
{{210, 0x3008}, "Sorcerous Skull"}, {{210, 0x3008}, "Sorcerous Skull"},
{{210, 0x300B}, "Axe of Illusion"}, {{210, 0x300B}, "Axe of Illusion"},
@ -301,15 +301,15 @@ static const std::map<const std::pair<const u16, const u16>, const std::string>
{{509, 0x0000}, "Small Fry"}, {{509, 0x0000}, "Small Fry"},
{{510, 0x0000}, "Drobit"}, {{510, 0x0000}, "Drobit"},
{{519, 0x0000}, "Trigger Snappy"}, {{519, 0x0000}, "Trigger Snappy"},
{{526, 0x0000}, "Whisper Elf"}, {{526, 0x3000}, "Whisper Elf"},
{{540, 0x0000}, "Barkley"}, {{540, 0x3000}, "Barkley"},
{{540, 0x3402}, "Gnarly Barkley"}, {{540, 0x3402}, "Gnarly Barkley"},
{{541, 0x0000}, "Thumpling"}, {{541, 0x3000}, "Thumpling"},
{{514, 0x0000}, "Gill Runt"}, {{514, 0x0000}, "Gill Runt"},
{{542, 0x0000}, "Mini-Jini"}, {{542, 0x3000}, "Mini-Jini"},
{{503, 0x0000}, "Spry"}, {{503, 0x0000}, "Spry"},
{{504, 0x0000}, "Hijinx"}, {{504, 0x0000}, "Hijinx"},
{{543, 0x0000}, "Eye Small"}, {{543, 0x1000}, "Eye Small"},
{{601, 0x0000}, "King Pen"}, {{601, 0x0000}, "King Pen"},
{{602, 0x0000}, "Tri-Tip"}, {{602, 0x0000}, "Tri-Tip"},
{{603, 0x0000}, "Chopscotch"}, {{603, 0x0000}, "Chopscotch"},
@ -419,15 +419,15 @@ static const std::map<const std::pair<const u16, const u16>, const std::string>
{{3013, 0x2206}, "LightCore Grim Creeper"}, {{3013, 0x2206}, "LightCore Grim Creeper"},
{{3014, 0x0000}, "Rip Tide"}, {{3014, 0x0000}, "Rip Tide"},
{{3015, 0x0000}, "Punk Shock"}, {{3015, 0x0000}, "Punk Shock"},
{{3200, 0x0000}, "Battle Hammer"}, {{3200, 0x2000}, "Battle Hammer"},
{{3201, 0x0000}, "Sky Diamond"}, {{3201, 0x2000}, "Sky Diamond"},
{{3202, 0x0000}, "Platinum Sheep"}, {{3202, 0x2000}, "Platinum Sheep"},
{{3203, 0x0000}, "Groove Machine"}, {{3203, 0x2000}, "Groove Machine"},
{{3204, 0x0000}, "UFO Hat"}, {{3204, 0x0000}, "UFO Hat"},
{{3300, 0x0000}, "Sheep Wreck Island"}, {{3300, 0x2000}, "Sheep Wreck Island"},
{{3301, 0x0000}, "Tower of Time"}, {{3301, 0x2000}, "Tower of Time"},
{{3302, 0x0000}, "Fiery Forge"}, {{3302, 0x2206}, "Fiery Forge"},
{{3303, 0x0000}, "Arkeyan Crossbow"}, {{3303, 0x2206}, "Arkeyan Crossbow"},
{{3220, 0x0000}, "Jet Stream"}, {{3220, 0x0000}, "Jet Stream"},
{{3221, 0x0000}, "Tomb Buggy"}, {{3221, 0x0000}, "Tomb Buggy"},
{{3222, 0x0000}, "Reef Ripper"}, {{3222, 0x0000}, "Reef Ripper"},

View file

@ -298,7 +298,7 @@ public:
const QString sdl = tr("The SDL handler supports a variety of controllers across different platforms."); const QString sdl = tr("The SDL handler supports a variety of controllers across different platforms.");
const QString orientation_reset = tr("Resets the sensor orientation when pressed.<br>Toggle the checkbox to enable or disable the orientation feature.<br>Currently only used for PS Move interactions."); const QString orientation_reset = tr("Resets the sensor orientation when pressed.<br>Toggle the checkbox to enable or disable the orientation feature.<br>Currently only used for PS Move interactions.");
const QString analog_limiter = tr("Applies the stick multipliers while this special button is pressed.<br>Enable \"Toggle\" if you want to toggle the analog limiter on button press instead."); const QString analog_limiter = tr("Applies the stick multipliers while this special button is pressed.<br>Enable \"Toggle\" if you want to toggle the analog limiter on button press instead.<br>If no button has been assigned, the stick multipliers are always applied.");
const QString pressure_intensity = tr("Controls the intensity of pressure sensitive buttons while this special button is pressed.<br>Enable \"Toggle\" if you want to toggle the intensity on button press instead.<br>Use the percentage to change how hard you want to press a button."); const QString pressure_intensity = tr("Controls the intensity of pressure sensitive buttons while this special button is pressed.<br>Enable \"Toggle\" if you want to toggle the intensity on button press instead.<br>Use the percentage to change how hard you want to press a button.");
const QString pressure_deadzone = tr("Controls the deadzone of pressure sensitive buttons. It determines how far the button has to be pressed until it is recognized by the game. The resulting range will be projected onto the full button sensitivity range."); const QString pressure_deadzone = tr("Controls the deadzone of pressure sensitive buttons. It determines how far the button has to be pressed until it is recognized by the game. The resulting range will be projected onto the full button sensitivity range.");
const QString squircle_factor = tr("The actual DualShock 3's stick range is not circular but formed like a rounded square (or squircle) which represents the maximum range of the emulated sticks. You can use the squircle values to modify the stick input if your sticks can't reach the corners of that range. A value of 0 does not apply any so called squircling. A value of 8000 is usually recommended."); const QString squircle_factor = tr("The actual DualShock 3's stick range is not circular but formed like a rounded square (or squircle) which represents the maximum range of the emulated sticks. You can use the squircle values to modify the stick input if your sticks can't reach the corners of that range. A value of 0 does not apply any so called squircling. A value of 8000 is usually recommended.");

View file

@ -814,7 +814,7 @@ void compressed_zstd_serialization_file_handler::initialize(utils::serial& ar)
// Make sure at least one thread is free // Make sure at least one thread is free
// Limit thread count in order to make sure memory limits are under control (TODO: scale with RAM size) // Limit thread count in order to make sure memory limits are under control (TODO: scale with RAM size)
const usz thread_count = std::min<u32>(std::max<u32>(utils::get_thread_count(), 2) - 1, 16); const usz thread_count = std::min<u32>(std::max<u32>(utils::get_thread_count(), 2) - 1, 32);
for (usz i = 0; i < thread_count; i++) for (usz i = 0; i < thread_count; i++)
{ {
@ -1132,50 +1132,37 @@ void compressed_zstd_serialization_file_handler::finalize(utils::serial& ar)
const stx::shared_ptr<std::vector<u8>> empty_data = stx::make_single<std::vector<u8>>(); const stx::shared_ptr<std::vector<u8>> empty_data = stx::make_single<std::vector<u8>>();
const stx::shared_ptr<std::vector<u8>> null_ptr = stx::null_ptr; const stx::shared_ptr<std::vector<u8>> null_ptr = stx::null_ptr;
for (bool has_pending_threads = true; has_pending_threads; thread_ctrl::wait_for(500))
{
has_pending_threads = false;
// Try to notify all in bulk
for (auto& context : m_compression_threads) for (auto& context : m_compression_threads)
{ {
// Try to notify all on the first iteration if (!context.notified && !context.m_input && context.m_input.compare_and_swap_test(null_ptr, empty_data))
if (context.m_input.compare_and_swap_test(null_ptr, empty_data)) {
context.notify_pending = true;
}
}
for (auto& context : m_compression_threads)
{
if (context.notify_pending)
{ {
context.notified = true; context.notified = true;
context.m_input.notify_one(); context.notify_pending = false;
context.m_input.notify_all();
} }
} }
for (auto& context : m_compression_threads) for (auto& context : m_compression_threads)
{ {
// Notify to abort // Wait for notification to be sent and received
while (!context.notified) // And wait for data to be written to be read by the thread
if (!context.notified || context.m_input || context.m_output)
{ {
const auto data = context.m_input.compare_and_swap(null_ptr, empty_data); has_pending_threads = true;
if (!data)
{
context.notified = true;
context.m_input.notify_one();
break;
} }
// Wait until valid input is processed
thread_ctrl::wait_for(1000);
}
}
for (auto& context : m_compression_threads)
{
// Wait for notification to be consumed
while (context.m_input)
{
thread_ctrl::wait_for(1000);
}
}
for (auto& context : m_compression_threads)
{
// Wait for data to be writen to be read by the thread
while (context.m_output)
{
thread_ctrl::wait_for(1000);
} }
} }

View file

@ -175,6 +175,7 @@ private:
{ {
atomic_ptr<std::vector<u8>> m_input; atomic_ptr<std::vector<u8>> m_input;
atomic_ptr<std::vector<u8>> m_output; atomic_ptr<std::vector<u8>> m_output;
bool notify_pending = false;
bool notified = false; bool notified = false;
std::unique_ptr<named_thread<std::function<void()>>> m_thread; std::unique_ptr<named_thread<std::function<void()>>> m_thread;
}; };

View file

@ -2213,8 +2213,11 @@ inline v128 gv_cvtu32_tofs(const v128& src)
#if defined(__AVX512VL__) #if defined(__AVX512VL__)
return _mm_cvtepu32_ps(src); return _mm_cvtepu32_ps(src);
#elif defined(ARCH_X64) #elif defined(ARCH_X64)
const auto fix = _mm_and_ps(_mm_castsi128_ps(_mm_srai_epi32(src, 31)), _mm_set1_ps(0x80000000)); constexpr u64 bit_shift = 9;
return _mm_add_ps(_mm_cvtepi32_ps(_mm_and_si128(src, _mm_set1_epi32(0x7fffffff))), fix); const auto shifted = _mm_srli_epi32(src, bit_shift);
const auto cleared = _mm_slli_epi32(shifted, bit_shift);
const auto low_bits = _mm_sub_epi32(src, cleared);
return _mm_add_ps(_mm_cvtepi32_ps(low_bits), _mm_mul_ps(_mm_cvtepi32_ps(shifted), _mm_set_ps1(1u << bit_shift)));
#elif defined(ARCH_ARM64) #elif defined(ARCH_ARM64)
return vcvtq_f32_u32(src); return vcvtq_f32_u32(src);
#endif #endif