mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 15:45:42 +00:00
Merge branch 'master' into patch-6
This commit is contained in:
commit
7100277ad5
28
.github/workflows/rolling-release.yml
vendored
28
.github/workflows/rolling-release.yml
vendored
|
@ -144,6 +144,12 @@ jobs:
|
|||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install packages
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
|
||||
|
||||
- name: Compile and zip Linux x64 libretro core
|
||||
shell: bash
|
||||
run: |
|
||||
|
@ -151,13 +157,28 @@ jobs:
|
|||
cd build-libretro-linux-x64
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_LIBRETRO_CORE=ON ..
|
||||
cmake --build . --parallel 2
|
||||
zip -j duckstation_libretro.so.zip duckstation_libretro.so
|
||||
zip -j duckstation_libretro_x64.so.zip duckstation_libretro.so
|
||||
|
||||
- name: Upload Linux x64 libretro core
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: "linux-libretro"
|
||||
path: "build-libretro-linux-x64/duckstation_libretro.so.zip"
|
||||
path: "build-libretro-linux-x64/duckstation_libretro_x64.so.zip"
|
||||
|
||||
- name: Compile and zip Linux AArch64 libretro core
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build-libretro-linux-aarch64
|
||||
cd build-libretro-linux-aarch64
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_LIBRETRO_CORE=ON -DCMAKE_TOOLCHAIN_FILE=../CMakeModules/aarch64-cross-toolchain.cmake ..
|
||||
cmake --build . --parallel 2
|
||||
zip -j duckstation_libretro_linux_aarch64.so.zip duckstation_libretro.so
|
||||
|
||||
- name: Upload Linux AArch64 libretro core
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: "linux-libretro"
|
||||
path: "build-libretro-linux-aarch64/duckstation_libretro_linux_aarch64.so.zip"
|
||||
|
||||
- name: Compile and zip Android AArch64 libretro core
|
||||
shell: bash
|
||||
|
@ -271,7 +292,8 @@ jobs:
|
|||
linux-x64-appimage-sdl-zsync/duckstation-sdl-x64.AppImage.zsync
|
||||
linux-x64-appimage-qt/duckstation-qt-x64.AppImage
|
||||
linux-x64-appimage-qt-zsync/duckstation-qt-x64.AppImage.zsync
|
||||
linux-libretro/duckstation_libretro.so.zip
|
||||
linux-libretro/duckstation_libretro_x64.so.zip
|
||||
linux-libretro/duckstation_libretro_linux_aarch64.so.zip
|
||||
linux-libretro/duckstation_libretro_android_aarch64.so.zip
|
||||
android/duckstation-android-aarch64.apk
|
||||
|
||||
|
|
14
CMakeModules/aarch64-cross-toolchain.cmake
Normal file
14
CMakeModules/aarch64-cross-toolchain.cmake
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Source: https://github.com/stenzek/duckstation/issues/626#issuecomment-660718306
|
||||
|
||||
# Target system
|
||||
SET(CMAKE_SYSTEM_NAME Linux)
|
||||
SET(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||
SET(CMAKE_SYSTEM_VERSION 1)
|
||||
set(CMAKE_CROSSCOMPILING TRUE)
|
||||
|
||||
# Cross compiler
|
||||
SET(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
|
||||
SET(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
|
||||
set(CMAKE_LIBRARY_ARCHITECTURE aarch64-linux-gnu)
|
||||
|
||||
set(THREADS_PTHREAD_ARG "0" CACHE STRING "Result from TRY_RUN" FORCE)
|
|
@ -12,6 +12,7 @@ The following people have contributed to the project in some way, and are credit
|
|||
- @phoe-nix - Chinese (Simplified)
|
||||
- Sorer - @MojoJojoDojo - Hebrew
|
||||
- Hipnosis183 - Spanish
|
||||
- @RaydenX93 - Italian
|
||||
|
||||
## Game Compatibility Database
|
||||
- @Zet-sensei
|
||||
|
|
|
@ -11,6 +11,8 @@ A "BIOS" ROM image is required to to start the emulator and to play games. You c
|
|||
|
||||
## Latest News
|
||||
|
||||
- 2020/08/20: Per-game setting overrides added. Mostly for compatibility, but some options are customizable.
|
||||
- 2020/08/19: CPU PGXP mode added. It is very slow and incompatible with the recompiler, only use for games which need it.
|
||||
- 2020/08/15: Playlist support/single memcard for multi-disc games in Qt frontend added.
|
||||
- 2020/08/07: Automatic updater for standalone Windows builds.
|
||||
- 2020/08/01: Initial PGXP (geometry/perspective correction) support.
|
||||
|
@ -36,6 +38,7 @@ Other features include:
|
|||
- CPU Recompiler/JIT (x86-64 and AArch64)
|
||||
- Hardware (D3D11, OpenGL, Vulkan) and software rendering
|
||||
- Upscaling and true colour (24-bit) in hardware renderers
|
||||
- PGXP for geometry precision and texture correction
|
||||
- "Fast boot" for skipping BIOS splash/intro
|
||||
- Save state support
|
||||
- Windows, Linux, **highly experimental** macOS support
|
||||
|
@ -228,7 +231,8 @@ DuckStation is available as a libretro core, which can be loaded into a frontend
|
|||
|
||||
Prebuilt binaries for 64-bit Windows, Linux and Android can be found on the releases page. Direct links:
|
||||
- 64-bit Windows: https://github.com/stenzek/duckstation/releases/download/latest/duckstation_libretro.dll.zip
|
||||
- 64-bit Linux: https://github.com/stenzek/duckstation/releases/download/latest/duckstation_libretro.so.zip
|
||||
- 64-bit Linux: https://github.com/stenzek/duckstation/releases/download/latest/duckstation_libretro_x64.so.zip
|
||||
- AArch64 Linux: https://github.com/stenzek/duckstation/releases/download/latest/duckstation_libretro_linux_aarch64.so.zip
|
||||
- AArch64 Android: https://github.com/stenzek/duckstation/releases/download/latest/duckstation_libretro_android_aarch64.so.zip
|
||||
|
||||
To use, download and extract, and install the core file in RetroArch or your preferred frontend.
|
||||
|
|
|
@ -132,6 +132,7 @@ void AndroidHostInterface::SetUserDirectory()
|
|||
void AndroidHostInterface::LoadSettings()
|
||||
{
|
||||
CommonHostInterface::LoadSettings(m_settings_interface);
|
||||
CommonHostInterface::FixIncompatibleSettings(false);
|
||||
CommonHostInterface::UpdateInputMap(m_settings_interface);
|
||||
}
|
||||
|
||||
|
|
|
@ -583,6 +583,10 @@
|
|||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1308-g622e50fa</version-tested>
|
||||
</entry>
|
||||
<entry code="SLUS-01144" compatibility="5" region="NTSC-U" title="Bugs Bunny & Taz - Time Busters (USA) (En,Fr,Es)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1529-ga895c027</version-tested>
|
||||
</entry>
|
||||
<entry code="SLUS-00838" compatibility="5" region="NTSC-U" title="Bugs Bunny - Lost in Time (USA) (En,Fr,Es)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1308-g622e50fa</version-tested>
|
||||
|
@ -591,6 +595,14 @@
|
|||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1115-g0b261e8</version-tested>
|
||||
</entry>
|
||||
<entry code="SCUS-94180" compatibility="5" region="NTSC-U" title="Bushido Blade (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1529-ga895c027</version-tested>
|
||||
</entry>
|
||||
<entry code="SLUS-00663" compatibility="5" region="NTSC-U" title="Bushido Blade 2 (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1529-ga895c027</version-tested>
|
||||
</entry>
|
||||
<entry code="SCUS-94263" compatibility="5" region="NTSC-U" title="Bust A Groove (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<upscaling-issues>When upscale the game 4x or above the green boxes (where the commands are showned) seems to be cutted there is a black line, 4 lines to more specific dividing the gren box.</upscaling-issues>
|
||||
|
@ -1314,11 +1326,40 @@ Tetris with Card Captor Sakura (Japan)
|
|||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-986-gfc911de1</version-tested>
|
||||
</entry>
|
||||
<entry code="SCUS-94242" compatibility="4" region="NTSC-U" title="Interactive CD Sampler Disc 6 (USA)">
|
||||
<compatibility>Graphical/Audio Issues</compatibility>
|
||||
<version-tested>0.1-1530-g6d75f42e</version-tested>
|
||||
<comments>Various graphical errors in menu (Issue #772).</comments>
|
||||
</entry>
|
||||
<entry code="PBPX-95011" compatibility="5" region="NTSC-U" title="Interactive CD Sampler Disc Vol. 10 (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1530-g6d75f42e</version-tested>
|
||||
</entry>
|
||||
<entry code="SCUS-94463" compatibility="5" region="NTSC-U" title="Interactive CD Sampler Disc Vol. 11 (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1530-g6d75f42e</version-tested>
|
||||
</entry>
|
||||
<entry code="PBPX-95010" compatibility="5" region="NTSC-U" title="Interactive CD Sampler Disc Vol. 9 (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1530-g6d75f42e</version-tested>
|
||||
</entry>
|
||||
<entry code="PBPX-95002" compatibility="3" region="NTSC-U" title="Interactive CD Sampler Disc Volume 4 (USA)">
|
||||
<compatibility>Crashes In-Game</compatibility>
|
||||
<version-tested>0.1-1490-g76978986</version-tested>
|
||||
<comments>Hangs when choosing Armored core (Issue #751).</comments>
|
||||
</entry>
|
||||
<entry code="PBPX-95006" compatibility="5" region="NTSC-U" title="Interactive CD Sampler Disc Volume 7 (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1530-g6d75f42e</version-tested>
|
||||
</entry>
|
||||
<entry code="PBPX-95009" compatibility="5" region="NTSC-U" title="Interactive CD Sampler Disc Volume 8 (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1530-g6d75f42e</version-tested>
|
||||
</entry>
|
||||
<entry code="PBPX-95003" compatibility="5" region="NTSC-U" title="Interactive CD Sampler Disk Volume 5 (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1530-g6d75f42e</version-tested>
|
||||
</entry>
|
||||
<entry code="SCUS-94957" compatibility="5" region="NTSC-U" title="Interactive CD Sampler Pack Volume 2 (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1490-g76978986</version-tested>
|
||||
|
@ -1623,6 +1664,10 @@ Tetris with Card Captor Sakura (Japan)
|
|||
<entry code="SLES-01485" compatibility="5" region="PAL" title="Mega Man Legends">
|
||||
<compatibility>No Issues</compatibility>
|
||||
</entry>
|
||||
<entry code="SLUS-01140" compatibility="5" region="NTSC-U" title="Mega Man Legends 2 (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1529-ga895c027</version-tested>
|
||||
</entry>
|
||||
<entry code="SLES-00503" compatibility="5" region="PAL" title="Mega Man X3">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-826-g712168c</version-tested>
|
||||
|
@ -2006,6 +2051,30 @@ Tetris with Card Captor Sakura (Japan)
|
|||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1445-ge5c46a54</version-tested>
|
||||
</entry>
|
||||
<entry code="SCUS-94482" compatibility="5" region="NTSC-U" title="PlayStation Demo Disc - Shock Your System! (USA) (SCUS-94482)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1530-g6d75f42e</version-tested>
|
||||
</entry>
|
||||
<entry code="SCUS-94618" compatibility="5" region="NTSC-U" title="PlayStation Demo Disc Version 1.3 (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1530-g6d75f42e</version-tested>
|
||||
</entry>
|
||||
<entry code="SCUS-94650" compatibility="5" region="NTSC-U" title="PlayStation Demo Disc Version 1.4 (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1530-g6d75f42e</version-tested>
|
||||
</entry>
|
||||
<entry code="SCUS-94674" compatibility="5" region="NTSC-U" title="PlayStation Demo Disc Version 1.5 (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1530-g6d75f42e</version-tested>
|
||||
</entry>
|
||||
<entry code="SCUS-94950" compatibility="5" region="NTSC-U" title="PlayStation Developer's Demo Disc (USA) (SCUS-94950)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1530-g6d75f42e</version-tested>
|
||||
</entry>
|
||||
<entry code="SCUS-94954" compatibility="5" region="NTSC-U" title="PlayStation Developer's Demo Disc (USA) (SCUS-94954)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1530-g6d75f42e</version-tested>
|
||||
</entry>
|
||||
<entry code="SLPS-01360" compatibility="5" region="NTSC-J" title="Pocket Fighter (Japan)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-884-g096ed21</version-tested>
|
||||
|
@ -2035,6 +2104,10 @@ Tetris with Card Captor Sakura (Japan)
|
|||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-774-g5a1b008</version-tested>
|
||||
</entry>
|
||||
<entry code="SLUS-01423" compatibility="5" region="NTSC-U" title="Powerpuff Girls, The - Chemical X-Traction (USA) (En,Es)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1529-ga895c027</version-tested>
|
||||
</entry>
|
||||
<entry code="SLUS-00102" compatibility="5" region="NTSC-U" title="Powerslave">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<upscaling-issues>2D elements on title screen</upscaling-issues>
|
||||
|
@ -2369,6 +2442,14 @@ Tetris with Card Captor Sakura (Japan)
|
|||
<entry code="SLPS-03444" compatibility="5" region="NTSC-J" title="Simple Character 2000 Series Vol. 08 - Kagaku Ninjatai Gatchaman - The Shooting (Japan)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
</entry>
|
||||
<entry code="SLUS-01227" compatibility="5" region="NTSC-U" title="Simpsons Wrestling, The (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1529-ga895c027</version-tested>
|
||||
</entry>
|
||||
<entry code="SLUS-00209" compatibility="5" region="NTSC-U" title="Skeleton Warriors (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1529-ga895c027</version-tested>
|
||||
</entry>
|
||||
<entry code="SLUS-00601" compatibility="5" region="NTSC-U" title="Skullmonkeys (USA)">
|
||||
<compatibility>No Issues</compatibility>
|
||||
<version-tested>0.1-1308-g622e50fa</version-tested>
|
||||
|
|
75
data/database/gamesettings.ini
Normal file
75
data/database/gamesettings.ini
Normal file
|
@ -0,0 +1,75 @@
|
|||
# DuckStation Game Settings
|
||||
|
||||
|
||||
# Croc - Legend of the Gobbos (USA) (SLUS-00530)
|
||||
[SLUS-00530]
|
||||
EnablePGXPCPUMode = true
|
||||
|
||||
|
||||
# Croc 2 (USA) (SLUS-00634)
|
||||
[SLUS-00634]
|
||||
EnablePGXPCPUMode = true
|
||||
|
||||
|
||||
# Doom (USA) (Rev 1) (SLUS-00077)
|
||||
[SLUS-00077]
|
||||
DisableUpscaling = true
|
||||
|
||||
|
||||
# Pop'n Music 6 (Japan) (SLPM-87089)
|
||||
[SLPM-87089]
|
||||
EnableInterlacing = true
|
||||
|
||||
|
||||
# Mr. Driller G (Japan) (SLPS-03336)
|
||||
[SLPS-03336]
|
||||
EnableInterlacing = true
|
||||
|
||||
|
||||
# Pro Pinball - Big Race USA (USA) (SLUS-01260)
|
||||
[SLUS-01260]
|
||||
ForceSoftwareRenderer = true
|
||||
EnableInterlacing = true
|
||||
|
||||
|
||||
# Pro Pinball - Fantastic Journey (USA) (SLUS-01261)
|
||||
[SLUS-01261]
|
||||
ForceSoftwareRenderer = true
|
||||
EnableInterlacing = true
|
||||
|
||||
|
||||
# True Pinball (USA) (SLUS-00337)
|
||||
[SLUS-00337]
|
||||
EnableInterlacing = true
|
||||
|
||||
|
||||
# Dead or Alive (USA) (SLUS-00606)
|
||||
[SLUS-00606]
|
||||
EnableInterlacing = true
|
||||
|
||||
|
||||
# Shinobi no Sato no Jintori Gassen (Japan) (SLPS-03553)
|
||||
[SLPS-03553]
|
||||
EnableInterlacing = true
|
||||
|
||||
|
||||
# Time Bokan Series: Bokan desu yo (SLPS-01211)
|
||||
[SLPS-01211]
|
||||
EnableInterlacing = true
|
||||
|
||||
|
||||
# Rat Attack! (USA) (SLUS-00656)
|
||||
[SLUS-00656]
|
||||
EnableInterlacing = true
|
||||
|
||||
|
||||
# Arcade Party Pak (USA) (SLUS-00952)
|
||||
[SLUS-00952]
|
||||
EnableInterlacing = true
|
||||
|
||||
|
||||
# SLUS-01222 (Colin McRae Rally 2.0 (USA) (En,Fr,Es))
|
||||
[SLUS-01222]
|
||||
DisplayActiveStartOffset = 64
|
||||
DisplayActiveEndOffset = 68
|
||||
|
|
@ -267,10 +267,12 @@ public:
|
|||
{
|
||||
#if WIN32
|
||||
// delete the temporary file
|
||||
if (!DeleteFileA(m_temporaryFileName.c_str()))
|
||||
if (!DeleteFileW(StringUtil::UTF8StringToWideString(m_temporaryFileName).c_str()))
|
||||
{
|
||||
Log_WarningPrintf(
|
||||
"AtomicUpdatedFileByteStream::~AtomicUpdatedFileByteStream(): Failed to delete temporary file '%s'",
|
||||
m_temporaryFileName.c_str());
|
||||
}
|
||||
#else
|
||||
// delete the temporary file
|
||||
if (remove(m_temporaryFileName.c_str()) < 0)
|
||||
|
@ -308,7 +310,8 @@ public:
|
|||
|
||||
#ifdef WIN32
|
||||
// move the atomic file name to the original file name
|
||||
if (!MoveFileExA(m_temporaryFileName.c_str(), m_originalFileName.c_str(), MOVEFILE_REPLACE_EXISTING))
|
||||
if (!MoveFileExW(StringUtil::UTF8StringToWideString(m_temporaryFileName).c_str(),
|
||||
StringUtil::UTF8StringToWideString(m_originalFileName).c_str(), MOVEFILE_REPLACE_EXISTING))
|
||||
{
|
||||
Log_WarningPrintf("AtomicUpdatedFileByteStream::Commit(): Failed to rename temporary file '%s' to '%s'",
|
||||
m_temporaryFileName.c_str(), m_originalFileName.c_str());
|
||||
|
|
|
@ -15,6 +15,13 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
// Force inline in non-debug helper
|
||||
#ifdef _DEBUG
|
||||
#define ALWAYS_INLINE_RELEASE
|
||||
#else
|
||||
#define ALWAYS_INLINE_RELEASE ALWAYS_INLINE
|
||||
#endif
|
||||
|
||||
// unreferenced parameter macro
|
||||
#ifndef UNREFERENCED_VARIABLE
|
||||
#if defined(_MSC_VER)
|
||||
|
|
|
@ -26,6 +26,8 @@ add_library(core
|
|||
dma.h
|
||||
game_list.cpp
|
||||
game_list.h
|
||||
game_settings.cpp
|
||||
game_settings.h
|
||||
gpu.cpp
|
||||
gpu.h
|
||||
gpu_commands.cpp
|
||||
|
@ -96,7 +98,7 @@ set(RECOMPILER_SRCS
|
|||
|
||||
target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
target_link_libraries(core PUBLIC Threads::Threads common imgui tinyxml2 zlib vulkan-loader)
|
||||
target_link_libraries(core PUBLIC Threads::Threads common imgui tinyxml2 zlib vulkan-loader simpleini)
|
||||
target_link_libraries(core PRIVATE glad stb)
|
||||
|
||||
if(WIN32)
|
||||
|
|
|
@ -53,8 +53,12 @@ bool AnalogController::DoState(StateWrapper& sw)
|
|||
|
||||
if (old_analog_mode != m_analog_mode)
|
||||
{
|
||||
g_host_interface->AddFormattedOSDMessage(5.0f, "Controller %u switched to %s mode.", m_index + 1u,
|
||||
m_analog_mode ? "analog" : "digital");
|
||||
g_host_interface->AddFormattedOSDMessage(
|
||||
5.0f,
|
||||
m_analog_mode ?
|
||||
g_host_interface->TranslateString("AnalogController", "Controller %u switched to analog mode.") :
|
||||
g_host_interface->TranslateString("AnalogController", "Controller %u switched to digital mode."),
|
||||
m_index + 1u);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -95,8 +99,13 @@ void AnalogController::SetButtonState(Button button, bool pressed)
|
|||
{
|
||||
if (m_analog_locked)
|
||||
{
|
||||
g_host_interface->AddFormattedOSDMessage(5.0f, "Controller %u is locked to %s mode by the game.", m_index + 1u,
|
||||
m_analog_mode ? "analog" : "digital");
|
||||
g_host_interface->AddFormattedOSDMessage(
|
||||
5.0f,
|
||||
m_analog_mode ? g_host_interface->TranslateString("AnalogController",
|
||||
"Controller %u is locked to analog mode by the game.") :
|
||||
g_host_interface->TranslateString("AnalogController",
|
||||
"Controller %u is locked to digital mode by the game."),
|
||||
m_index + 1u);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -155,8 +164,11 @@ void AnalogController::SetAnalogMode(bool enabled)
|
|||
return;
|
||||
|
||||
Log_InfoPrintf("Controller %u switched to %s mode.", m_index + 1u, enabled ? "analog" : "digital");
|
||||
g_host_interface->AddFormattedOSDMessage(5.0f, "Controller %u switched to %s mode.", m_index + 1u,
|
||||
enabled ? "analog" : "digital");
|
||||
g_host_interface->AddFormattedOSDMessage(
|
||||
5.0f,
|
||||
enabled ? g_host_interface->TranslateString("AnalogController", "Controller %u switched to analog mode.") :
|
||||
g_host_interface->TranslateString("AnalogController", "Controller %u switched to digital mode."),
|
||||
m_index + 1u);
|
||||
m_analog_mode = enabled;
|
||||
}
|
||||
|
||||
|
|
|
@ -1917,6 +1917,17 @@ void CDROM::DoSectorRead()
|
|||
|
||||
// TODO: Error handling
|
||||
const CDImage::SubChannelQ& subq = m_reader.GetSectorSubQ();
|
||||
if (subq.IsCRCValid())
|
||||
{
|
||||
m_last_subq = subq;
|
||||
}
|
||||
else
|
||||
{
|
||||
const CDImage::Position pos(CDImage::Position::FromLBA(m_current_lba));
|
||||
Log_DevPrintf("Sector %u [%02u:%02u:%02u] has invalid subchannel Q", m_current_lba, pos.minute, pos.second,
|
||||
pos.frame);
|
||||
}
|
||||
|
||||
if (subq.track_number_bcd == CDImage::LEAD_OUT_TRACK_NUMBER)
|
||||
{
|
||||
Log_DevPrintf("Read reached lead-out area of disc at LBA %u, pausing", m_reader.GetLastReadSector());
|
||||
|
@ -1948,17 +1959,6 @@ void CDROM::DoSectorRead()
|
|||
ProcessDataSectorHeader(m_reader.GetSectorBuffer().data());
|
||||
}
|
||||
|
||||
if (subq.IsCRCValid())
|
||||
{
|
||||
m_last_subq = subq;
|
||||
}
|
||||
else
|
||||
{
|
||||
const CDImage::Position pos(CDImage::Position::FromLBA(m_current_lba));
|
||||
Log_DevPrintf("Sector %u [%02u:%02u:%02u] has invalid subchannel Q", m_current_lba, pos.minute, pos.second,
|
||||
pos.frame);
|
||||
}
|
||||
|
||||
if (is_data_sector && m_drive_state == DriveState::Reading)
|
||||
{
|
||||
ProcessDataSector(m_reader.GetSectorBuffer().data(), subq);
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
<ClCompile Include="cpu_types.cpp" />
|
||||
<ClCompile Include="digital_controller.cpp" />
|
||||
<ClCompile Include="game_list.cpp" />
|
||||
<ClCompile Include="game_settings.cpp" />
|
||||
<ClCompile Include="gpu_commands.cpp" />
|
||||
<ClCompile Include="gpu_hw_d3d11.cpp" />
|
||||
<ClCompile Include="gpu_hw_shadergen.cpp" />
|
||||
|
@ -107,6 +108,7 @@
|
|||
<ClInclude Include="cpu_recompiler_types.h" />
|
||||
<ClInclude Include="digital_controller.h" />
|
||||
<ClInclude Include="game_list.h" />
|
||||
<ClInclude Include="game_settings.h" />
|
||||
<ClInclude Include="gpu_hw_d3d11.h" />
|
||||
<ClInclude Include="gpu_hw_shadergen.h" />
|
||||
<ClInclude Include="gpu_hw_vulkan.h" />
|
||||
|
@ -142,6 +144,12 @@
|
|||
<ClInclude Include="types.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\dep\imgui\imgui.vcxproj">
|
||||
<Project>{bb08260f-6fbc-46af-8924-090ee71360c6}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\dep\simpleini\simpleini.vcxproj">
|
||||
<Project>{3773f4cc-614e-4028-8595-22e08ca649e3}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\dep\stb\stb.vcxproj">
|
||||
<Project>{ed601289-ac1a-46b8-a8ed-17db9eb73423}</Project>
|
||||
</ProjectReference>
|
||||
|
@ -299,7 +307,7 @@
|
|||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -325,7 +333,7 @@
|
|||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -351,7 +359,7 @@
|
|||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
|
@ -380,7 +388,7 @@
|
|||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
|
@ -408,7 +416,7 @@
|
|||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -435,7 +443,7 @@
|
|||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -463,7 +471,7 @@
|
|||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -490,7 +498,7 @@
|
|||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
<ClCompile Include="resources.cpp" />
|
||||
<ClCompile Include="host_interface_progress_callback.cpp" />
|
||||
<ClCompile Include="pgxp.cpp" />
|
||||
<ClCompile Include="game_settings.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="types.h" />
|
||||
|
@ -97,5 +98,6 @@
|
|||
<ClInclude Include="gte_types.h" />
|
||||
<ClInclude Include="pgxp.h" />
|
||||
<ClInclude Include="cpu_core_private.h" />
|
||||
<ClInclude Include="game_settings.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -110,7 +110,8 @@ void Shutdown()
|
|||
#endif
|
||||
}
|
||||
|
||||
void Execute()
|
||||
template<PGXPMode pgxp_mode>
|
||||
static void ExecuteImpl()
|
||||
{
|
||||
CodeBlockKey next_block_key;
|
||||
|
||||
|
@ -157,7 +158,7 @@ void Execute()
|
|||
}
|
||||
else
|
||||
{
|
||||
InterpretCachedBlock(*block);
|
||||
InterpretCachedBlock<pgxp_mode>(*block);
|
||||
}
|
||||
|
||||
if (g_state.pending_ticks >= g_state.downcount)
|
||||
|
@ -212,6 +213,21 @@ void Execute()
|
|||
g_state.regs.npc = g_state.regs.pc;
|
||||
}
|
||||
|
||||
void Execute()
|
||||
{
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
{
|
||||
if (g_settings.gpu_pgxp_cpu)
|
||||
ExecuteImpl<PGXPMode::CPU>();
|
||||
else
|
||||
ExecuteImpl<PGXPMode::Memory>();
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecuteImpl<PGXPMode::Disabled>();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_RECOMPILER
|
||||
|
||||
void ExecuteRecompiler()
|
||||
|
|
|
@ -96,6 +96,7 @@ void SetUseRecompiler(bool enable);
|
|||
/// Invalidates all blocks which are in the range of the specified code page.
|
||||
void InvalidateBlocksWithPageIndex(u32 page_index);
|
||||
|
||||
template<PGXPMode pgxp_mode>
|
||||
void InterpretCachedBlock(const CodeBlock& block);
|
||||
void InterpretUncachedBlock();
|
||||
|
||||
|
|
|
@ -15,19 +15,9 @@ Log_SetChannel(CPU::Core);
|
|||
|
||||
namespace CPU {
|
||||
|
||||
/// Sets the PC and flushes the pipeline.
|
||||
static void SetPC(u32 new_pc);
|
||||
|
||||
// Updates load delays - call after each instruction
|
||||
static void UpdateLoadDelay();
|
||||
|
||||
// Fetches the instruction at m_regs.npc
|
||||
static void ExecuteInstruction();
|
||||
static void ExecuteCop0Instruction();
|
||||
static void ExecuteCop2Instruction();
|
||||
static void Branch(u32 target);
|
||||
|
||||
// clears pipeline of load/branch delays
|
||||
static void FlushPipeline();
|
||||
|
||||
State g_state;
|
||||
|
@ -139,14 +129,14 @@ bool DoState(StateWrapper& sw)
|
|||
return !sw.HasError();
|
||||
}
|
||||
|
||||
void SetPC(u32 new_pc)
|
||||
ALWAYS_INLINE_RELEASE void SetPC(u32 new_pc)
|
||||
{
|
||||
DebugAssert(Common::IsAlignedPow2(new_pc, 4));
|
||||
g_state.regs.npc = new_pc;
|
||||
FlushPipeline();
|
||||
}
|
||||
|
||||
void Branch(u32 target)
|
||||
ALWAYS_INLINE_RELEASE void Branch(u32 target)
|
||||
{
|
||||
if (!Common::IsAlignedPow2(target, 4))
|
||||
{
|
||||
|
@ -240,7 +230,7 @@ void ClearExternalInterrupt(u8 bit)
|
|||
g_state.cop0_regs.cause.Ip &= static_cast<u8>(~(1u << bit));
|
||||
}
|
||||
|
||||
void UpdateLoadDelay()
|
||||
ALWAYS_INLINE_RELEASE static void UpdateLoadDelay()
|
||||
{
|
||||
// the old value is needed in case the delay slot instruction overwrites the same register
|
||||
if (g_state.load_delay_reg != Reg::count)
|
||||
|
@ -251,7 +241,7 @@ void UpdateLoadDelay()
|
|||
g_state.next_load_delay_reg = Reg::count;
|
||||
}
|
||||
|
||||
void FlushPipeline()
|
||||
ALWAYS_INLINE_RELEASE static void FlushPipeline()
|
||||
{
|
||||
// loads are flushed
|
||||
g_state.next_load_delay_reg = Reg::count;
|
||||
|
@ -275,12 +265,12 @@ void FlushPipeline()
|
|||
g_state.current_instruction_was_branch_taken = false;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE u32 ReadReg(Reg rs)
|
||||
ALWAYS_INLINE static u32 ReadReg(Reg rs)
|
||||
{
|
||||
return g_state.regs.r[static_cast<u8>(rs)];
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void WriteReg(Reg rd, u32 value)
|
||||
ALWAYS_INLINE static void WriteReg(Reg rd, u32 value)
|
||||
{
|
||||
g_state.regs.r[static_cast<u8>(rd)] = value;
|
||||
g_state.load_delay_reg = (rd == g_state.load_delay_reg) ? Reg::count : g_state.load_delay_reg;
|
||||
|
@ -289,7 +279,7 @@ ALWAYS_INLINE void WriteReg(Reg rd, u32 value)
|
|||
g_state.regs.zero = 0;
|
||||
}
|
||||
|
||||
static void WriteRegDelayed(Reg rd, u32 value)
|
||||
ALWAYS_INLINE_RELEASE static void WriteRegDelayed(Reg rd, u32 value)
|
||||
{
|
||||
Assert(g_state.next_load_delay_reg == Reg::count);
|
||||
if (rd == Reg::zero)
|
||||
|
@ -304,7 +294,7 @@ static void WriteRegDelayed(Reg rd, u32 value)
|
|||
g_state.next_load_delay_value = value;
|
||||
}
|
||||
|
||||
static std::optional<u32> ReadCop0Reg(Cop0Reg reg)
|
||||
ALWAYS_INLINE_RELEASE static std::optional<u32> ReadCop0Reg(Cop0Reg reg)
|
||||
{
|
||||
switch (reg)
|
||||
{
|
||||
|
@ -347,7 +337,7 @@ static std::optional<u32> ReadCop0Reg(Cop0Reg reg)
|
|||
}
|
||||
}
|
||||
|
||||
static void WriteCop0Reg(Cop0Reg reg, u32 value)
|
||||
ALWAYS_INLINE_RELEASE static void WriteCop0Reg(Cop0Reg reg, u32 value)
|
||||
{
|
||||
switch (reg)
|
||||
{
|
||||
|
@ -431,12 +421,12 @@ static void LogInstruction(u32 bits, u32 pc, Registers* regs)
|
|||
WriteToExecutionLog("%08x: %08x %s\n", pc, bits, instr.GetCharArray());
|
||||
}
|
||||
|
||||
static constexpr bool AddOverflow(u32 old_value, u32 add_value, u32 new_value)
|
||||
ALWAYS_INLINE static constexpr bool AddOverflow(u32 old_value, u32 add_value, u32 new_value)
|
||||
{
|
||||
return (((new_value ^ old_value) & (new_value ^ add_value)) & UINT32_C(0x80000000)) != 0;
|
||||
}
|
||||
|
||||
static constexpr bool SubOverflow(u32 old_value, u32 sub_value, u32 new_value)
|
||||
ALWAYS_INLINE static constexpr bool SubOverflow(u32 old_value, u32 sub_value, u32 new_value)
|
||||
{
|
||||
return (((new_value ^ old_value) & (old_value ^ sub_value)) & UINT32_C(0x80000000)) != 0;
|
||||
}
|
||||
|
@ -467,53 +457,8 @@ void DisassembleAndPrint(u32 addr, u32 instructions_before /* = 0 */, u32 instru
|
|||
}
|
||||
}
|
||||
|
||||
void Execute()
|
||||
{
|
||||
g_state.frame_done = false;
|
||||
while (!g_state.frame_done)
|
||||
{
|
||||
TimingEvents::UpdateCPUDowncount();
|
||||
|
||||
while (g_state.pending_ticks <= g_state.downcount)
|
||||
{
|
||||
if (HasPendingInterrupt())
|
||||
DispatchInterrupt();
|
||||
|
||||
g_state.pending_ticks++;
|
||||
|
||||
// now executing the instruction we previously fetched
|
||||
g_state.current_instruction.bits = g_state.next_instruction.bits;
|
||||
g_state.current_instruction_pc = g_state.regs.pc;
|
||||
g_state.current_instruction_in_branch_delay_slot = g_state.next_instruction_is_branch_delay_slot;
|
||||
g_state.current_instruction_was_branch_taken = g_state.branch_was_taken;
|
||||
g_state.next_instruction_is_branch_delay_slot = false;
|
||||
g_state.branch_was_taken = false;
|
||||
g_state.exception_raised = false;
|
||||
|
||||
// fetch the next instruction
|
||||
if (!FetchInstruction())
|
||||
continue;
|
||||
|
||||
#if 0 // GTE flag test debugging
|
||||
if (g_state.m_current_instruction_pc == 0x8002cdf4)
|
||||
{
|
||||
if (g_state.m_regs.v1 != g_state.m_regs.v0)
|
||||
printf("Got %08X Expected? %08X\n", g_state.m_regs.v1, g_state.m_regs.v0);
|
||||
}
|
||||
#endif
|
||||
|
||||
// execute the instruction we previously fetched
|
||||
ExecuteInstruction();
|
||||
|
||||
// next load delay
|
||||
UpdateLoadDelay();
|
||||
}
|
||||
|
||||
TimingEvents::RunEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void ExecuteInstruction()
|
||||
template<PGXPMode pgxp_mode>
|
||||
ALWAYS_INLINE_RELEASE static void ExecuteInstruction()
|
||||
{
|
||||
const Instruction inst = g_state.current_instruction;
|
||||
|
||||
|
@ -525,14 +470,6 @@ void ExecuteInstruction()
|
|||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
if (g_state.m_current_instruction_pc == 0x8002bf50)
|
||||
{
|
||||
TRACE_EXECUTION = true;
|
||||
__debugbreak();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (TRACE_EXECUTION)
|
||||
PrintInstruction(inst.bits, g_state.current_instruction_pc, &g_state.regs);
|
||||
|
@ -540,6 +477,10 @@ void ExecuteInstruction()
|
|||
LogInstruction(inst.bits, g_state.current_instruction_pc, &g_state.regs);
|
||||
#endif
|
||||
|
||||
// Skip nops. Makes PGXP-CPU quicker, but also the regular interpreter.
|
||||
if (inst.bits == 0)
|
||||
return;
|
||||
|
||||
switch (inst.op)
|
||||
{
|
||||
case InstructionOp::funct:
|
||||
|
@ -549,6 +490,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::sll:
|
||||
{
|
||||
const u32 new_value = ReadReg(inst.r.rt) << inst.r.shamt;
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SLL(inst.bits, new_value, ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -556,6 +500,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::srl:
|
||||
{
|
||||
const u32 new_value = ReadReg(inst.r.rt) >> inst.r.shamt;
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SRL(inst.bits, new_value, ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -563,6 +510,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::sra:
|
||||
{
|
||||
const u32 new_value = static_cast<u32>(static_cast<s32>(ReadReg(inst.r.rt)) >> inst.r.shamt);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SRA(inst.bits, new_value, ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -571,6 +521,9 @@ void ExecuteInstruction()
|
|||
{
|
||||
const u32 shift_amount = ReadReg(inst.r.rs) & UINT32_C(0x1F);
|
||||
const u32 new_value = ReadReg(inst.r.rt) << shift_amount;
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SLLV(inst.bits, new_value, ReadReg(inst.r.rt), shift_amount);
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -579,6 +532,9 @@ void ExecuteInstruction()
|
|||
{
|
||||
const u32 shift_amount = ReadReg(inst.r.rs) & UINT32_C(0x1F);
|
||||
const u32 new_value = ReadReg(inst.r.rt) >> shift_amount;
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SRLV(inst.bits, new_value, ReadReg(inst.r.rt), shift_amount);
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -587,6 +543,9 @@ void ExecuteInstruction()
|
|||
{
|
||||
const u32 shift_amount = ReadReg(inst.r.rs) & UINT32_C(0x1F);
|
||||
const u32 new_value = static_cast<u32>(static_cast<s32>(ReadReg(inst.r.rt)) >> shift_amount);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SRAV(inst.bits, new_value, ReadReg(inst.r.rt), shift_amount);
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -594,6 +553,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::and_:
|
||||
{
|
||||
const u32 new_value = ReadReg(inst.r.rs) & ReadReg(inst.r.rt);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_AND_(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -601,6 +563,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::or_:
|
||||
{
|
||||
const u32 new_value = ReadReg(inst.r.rs) | ReadReg(inst.r.rt);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_OR_(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -608,6 +573,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::xor_:
|
||||
{
|
||||
const u32 new_value = ReadReg(inst.r.rs) ^ ReadReg(inst.r.rt);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_XOR_(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -615,6 +583,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::nor:
|
||||
{
|
||||
const u32 new_value = ~(ReadReg(inst.r.rs) | ReadReg(inst.r.rt));
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_NOR(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -630,6 +601,9 @@ void ExecuteInstruction()
|
|||
return;
|
||||
}
|
||||
|
||||
if constexpr (pgxp_mode == PGXPMode::CPU)
|
||||
PGXP::CPU_ADD(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -637,6 +611,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::addu:
|
||||
{
|
||||
const u32 new_value = ReadReg(inst.r.rs) + ReadReg(inst.r.rt);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_ADDU(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -652,6 +629,9 @@ void ExecuteInstruction()
|
|||
return;
|
||||
}
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SUB(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -659,6 +639,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::subu:
|
||||
{
|
||||
const u32 new_value = ReadReg(inst.r.rs) - ReadReg(inst.r.rt);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SUBU(inst.bits, new_value, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, new_value);
|
||||
}
|
||||
break;
|
||||
|
@ -666,6 +649,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::slt:
|
||||
{
|
||||
const u32 result = BoolToUInt32(static_cast<s32>(ReadReg(inst.r.rs)) < static_cast<s32>(ReadReg(inst.r.rt)));
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SLT(inst.bits, result, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, result);
|
||||
}
|
||||
break;
|
||||
|
@ -673,12 +659,18 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::sltu:
|
||||
{
|
||||
const u32 result = BoolToUInt32(ReadReg(inst.r.rs) < ReadReg(inst.r.rt));
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SLTU(inst.bits, result, ReadReg(inst.r.rs), ReadReg(inst.r.rt));
|
||||
|
||||
WriteReg(inst.r.rd, result);
|
||||
}
|
||||
break;
|
||||
|
||||
case InstructionFunct::mfhi:
|
||||
{
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_MFHI(inst.bits, ReadReg(inst.r.rd), g_state.regs.hi);
|
||||
|
||||
WriteReg(inst.r.rd, g_state.regs.hi);
|
||||
}
|
||||
break;
|
||||
|
@ -686,12 +678,18 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::mthi:
|
||||
{
|
||||
const u32 value = ReadReg(inst.r.rs);
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_MTHI(inst.bits, g_state.regs.hi, value);
|
||||
|
||||
g_state.regs.hi = value;
|
||||
}
|
||||
break;
|
||||
|
||||
case InstructionFunct::mflo:
|
||||
{
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_MFLO(inst.bits, ReadReg(inst.r.rd), g_state.regs.lo);
|
||||
|
||||
WriteReg(inst.r.rd, g_state.regs.lo);
|
||||
}
|
||||
break;
|
||||
|
@ -699,6 +697,9 @@ void ExecuteInstruction()
|
|||
case InstructionFunct::mtlo:
|
||||
{
|
||||
const u32 value = ReadReg(inst.r.rs);
|
||||
if constexpr (pgxp_mode == PGXPMode::CPU)
|
||||
PGXP::CPU_MTLO(inst.bits, g_state.regs.lo, value);
|
||||
|
||||
g_state.regs.lo = value;
|
||||
}
|
||||
break;
|
||||
|
@ -709,8 +710,12 @@ void ExecuteInstruction()
|
|||
const u32 rhs = ReadReg(inst.r.rt);
|
||||
const u64 result =
|
||||
static_cast<u64>(static_cast<s64>(SignExtend64(lhs)) * static_cast<s64>(SignExtend64(rhs)));
|
||||
|
||||
g_state.regs.hi = Truncate32(result >> 32);
|
||||
g_state.regs.lo = Truncate32(result);
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_MULT(inst.bits, g_state.regs.hi, g_state.regs.lo, lhs, rhs);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -719,6 +724,10 @@ void ExecuteInstruction()
|
|||
const u32 lhs = ReadReg(inst.r.rs);
|
||||
const u32 rhs = ReadReg(inst.r.rt);
|
||||
const u64 result = ZeroExtend64(lhs) * ZeroExtend64(rhs);
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_MULTU(inst.bits, g_state.regs.hi, g_state.regs.lo, lhs, rhs);
|
||||
|
||||
g_state.regs.hi = Truncate32(result >> 32);
|
||||
g_state.regs.lo = Truncate32(result);
|
||||
}
|
||||
|
@ -746,6 +755,9 @@ void ExecuteInstruction()
|
|||
g_state.regs.lo = static_cast<u32>(num / denom);
|
||||
g_state.regs.hi = static_cast<u32>(num % denom);
|
||||
}
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_DIV(inst.bits, g_state.regs.hi, g_state.regs.lo, num, denom);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -765,6 +777,9 @@ void ExecuteInstruction()
|
|||
g_state.regs.lo = num / denom;
|
||||
g_state.regs.hi = num % denom;
|
||||
}
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_DIVU(inst.bits, g_state.regs.hi, g_state.regs.lo, num, denom);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -808,25 +823,44 @@ void ExecuteInstruction()
|
|||
|
||||
case InstructionOp::lui:
|
||||
{
|
||||
WriteReg(inst.i.rt, inst.i.imm_zext32() << 16);
|
||||
const u32 value = inst.i.imm_zext32() << 16;
|
||||
WriteReg(inst.i.rt, value);
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_LUI(inst.bits, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case InstructionOp::andi:
|
||||
{
|
||||
WriteReg(inst.i.rt, ReadReg(inst.i.rs) & inst.i.imm_zext32());
|
||||
const u32 new_value = ReadReg(inst.i.rs) & inst.i.imm_zext32();
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_ANDI(inst.bits, new_value, ReadReg(inst.i.rs));
|
||||
|
||||
WriteReg(inst.i.rt, new_value);
|
||||
}
|
||||
break;
|
||||
|
||||
case InstructionOp::ori:
|
||||
{
|
||||
WriteReg(inst.i.rt, ReadReg(inst.i.rs) | inst.i.imm_zext32());
|
||||
const u32 new_value = ReadReg(inst.i.rs) | inst.i.imm_zext32();
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_ORI(inst.bits, new_value, ReadReg(inst.i.rs));
|
||||
|
||||
WriteReg(inst.i.rt, new_value);
|
||||
}
|
||||
break;
|
||||
|
||||
case InstructionOp::xori:
|
||||
{
|
||||
WriteReg(inst.i.rt, ReadReg(inst.i.rs) ^ inst.i.imm_zext32());
|
||||
const u32 new_value = ReadReg(inst.i.rs) ^ inst.i.imm_zext32();
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_XORI(inst.bits, new_value, ReadReg(inst.i.rs));
|
||||
|
||||
WriteReg(inst.i.rt, new_value);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -841,19 +875,31 @@ void ExecuteInstruction()
|
|||
return;
|
||||
}
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_ANDI(inst.bits, new_value, ReadReg(inst.i.rs));
|
||||
|
||||
WriteReg(inst.i.rt, new_value);
|
||||
}
|
||||
break;
|
||||
|
||||
case InstructionOp::addiu:
|
||||
{
|
||||
WriteReg(inst.i.rt, ReadReg(inst.i.rs) + inst.i.imm_sext32());
|
||||
const u32 new_value = ReadReg(inst.i.rs) + inst.i.imm_sext32();
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_ADDIU(inst.bits, new_value, ReadReg(inst.i.rs));
|
||||
|
||||
WriteReg(inst.i.rt, new_value);
|
||||
}
|
||||
break;
|
||||
|
||||
case InstructionOp::slti:
|
||||
{
|
||||
const u32 result = BoolToUInt32(static_cast<s32>(ReadReg(inst.i.rs)) < static_cast<s32>(inst.i.imm_sext32()));
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SLTI(inst.bits, result, ReadReg(inst.i.rs));
|
||||
|
||||
WriteReg(inst.i.rt, result);
|
||||
}
|
||||
break;
|
||||
|
@ -861,6 +907,10 @@ void ExecuteInstruction()
|
|||
case InstructionOp::sltiu:
|
||||
{
|
||||
const u32 result = BoolToUInt32(ReadReg(inst.i.rs) < inst.i.imm_sext32());
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::CPU)
|
||||
PGXP::CPU_SLTIU(inst.bits, result, ReadReg(inst.i.rs));
|
||||
|
||||
WriteReg(inst.i.rt, result);
|
||||
}
|
||||
break;
|
||||
|
@ -876,7 +926,7 @@ void ExecuteInstruction()
|
|||
|
||||
WriteRegDelayed(inst.i.rt, sxvalue);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_LBx(inst.bits, sxvalue, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -891,7 +941,7 @@ void ExecuteInstruction()
|
|||
const u32 sxvalue = SignExtend32(value);
|
||||
WriteRegDelayed(inst.i.rt, sxvalue);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_LHx(inst.bits, sxvalue, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -905,7 +955,7 @@ void ExecuteInstruction()
|
|||
|
||||
WriteRegDelayed(inst.i.rt, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_LW(inst.bits, value, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -920,7 +970,7 @@ void ExecuteInstruction()
|
|||
const u32 zxvalue = ZeroExtend32(value);
|
||||
WriteRegDelayed(inst.i.rt, zxvalue);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_LBx(inst.bits, zxvalue, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -935,7 +985,7 @@ void ExecuteInstruction()
|
|||
const u32 zxvalue = ZeroExtend32(value);
|
||||
WriteRegDelayed(inst.i.rt, zxvalue);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_LHx(inst.bits, zxvalue, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -966,7 +1016,7 @@ void ExecuteInstruction()
|
|||
|
||||
WriteRegDelayed(inst.i.rt, new_value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_LW(inst.bits, new_value, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -977,7 +1027,7 @@ void ExecuteInstruction()
|
|||
const u8 value = Truncate8(ReadReg(inst.i.rt));
|
||||
WriteMemoryByte(addr, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_SB(inst.bits, value, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -988,7 +1038,7 @@ void ExecuteInstruction()
|
|||
const u16 value = Truncate16(ReadReg(inst.i.rt));
|
||||
WriteMemoryHalfWord(addr, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_SH(inst.bits, value, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -999,7 +1049,7 @@ void ExecuteInstruction()
|
|||
const u32 value = ReadReg(inst.i.rt);
|
||||
WriteMemoryWord(addr, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_SW(inst.bits, value, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -1029,7 +1079,7 @@ void ExecuteInstruction()
|
|||
|
||||
WriteMemoryWord(aligned_addr, new_value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_SW(inst.bits, new_value, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -1114,7 +1164,58 @@ void ExecuteInstruction()
|
|||
return;
|
||||
}
|
||||
|
||||
ExecuteCop0Instruction();
|
||||
if (inst.cop.IsCommonInstruction())
|
||||
{
|
||||
switch (inst.cop.CommonOp())
|
||||
{
|
||||
case CopCommonInstruction::mfcn:
|
||||
{
|
||||
const std::optional<u32> value = ReadCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue()));
|
||||
|
||||
if constexpr (pgxp_mode == PGXPMode::CPU)
|
||||
PGXP::CPU_MFC0(inst.bits, value.value_or(0), ReadReg(inst.i.rs));
|
||||
|
||||
if (value)
|
||||
WriteRegDelayed(inst.r.rt, value.value());
|
||||
else
|
||||
RaiseException(Exception::RI);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::mtcn:
|
||||
{
|
||||
WriteCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue()), ReadReg(inst.r.rt));
|
||||
|
||||
if constexpr (pgxp_mode == PGXPMode::CPU)
|
||||
{
|
||||
PGXP::CPU_MTC0(inst.bits, ReadCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue())).value_or(0),
|
||||
ReadReg(inst.i.rs));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Panic("Missing implementation");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (inst.cop.Cop0Op())
|
||||
{
|
||||
case Cop0Instruction::rfe:
|
||||
{
|
||||
// restore mode
|
||||
g_state.cop0_regs.sr.mode_bits =
|
||||
(g_state.cop0_regs.sr.mode_bits & UINT32_C(0b110000)) | (g_state.cop0_regs.sr.mode_bits >> 2);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Panic("Missing implementation");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1127,7 +1228,61 @@ void ExecuteInstruction()
|
|||
return;
|
||||
}
|
||||
|
||||
ExecuteCop2Instruction();
|
||||
if (inst.cop.IsCommonInstruction())
|
||||
{
|
||||
// TODO: Combine with cop0.
|
||||
switch (inst.cop.CommonOp())
|
||||
{
|
||||
case CopCommonInstruction::cfcn:
|
||||
{
|
||||
const u32 value = GTE::ReadRegister(static_cast<u32>(inst.r.rd.GetValue()) + 32);
|
||||
WriteRegDelayed(inst.r.rt, value);
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_CFC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::ctcn:
|
||||
{
|
||||
const u32 value = ReadReg(inst.r.rt);
|
||||
GTE::WriteRegister(static_cast<u32>(inst.r.rd.GetValue()) + 32, value);
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_CTC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::mfcn:
|
||||
{
|
||||
const u32 value = GTE::ReadRegister(static_cast<u32>(inst.r.rd.GetValue()));
|
||||
WriteRegDelayed(inst.r.rt, value);
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_MFC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::mtcn:
|
||||
{
|
||||
const u32 value = ReadReg(inst.r.rt);
|
||||
GTE::WriteRegister(static_cast<u32>(inst.r.rd.GetValue()), value);
|
||||
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_MTC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::bcnc:
|
||||
default:
|
||||
Panic("Missing implementation");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GTE::ExecuteInstruction(inst.bits);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1147,7 +1302,7 @@ void ExecuteInstruction()
|
|||
|
||||
GTE::WriteRegister(ZeroExtend32(static_cast<u8>(inst.i.rt.GetValue())), value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_LWC2(inst.bits, value, addr);
|
||||
}
|
||||
break;
|
||||
|
@ -1165,12 +1320,12 @@ void ExecuteInstruction()
|
|||
const u32 value = GTE::ReadRegister(ZeroExtend32(static_cast<u8>(inst.i.rt.GetValue())));
|
||||
WriteMemoryWord(addr, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
if constexpr (pgxp_mode >= PGXPMode::Memory)
|
||||
PGXP::CPU_SWC2(inst.bits, value, addr);
|
||||
}
|
||||
break;
|
||||
|
||||
// swc0/lwc0/cop1/cop3 are essentially no-ops
|
||||
// swc0/lwc0/cop1/cop3 are essentially no-ops
|
||||
case InstructionOp::cop1:
|
||||
case InstructionOp::cop3:
|
||||
case InstructionOp::lwc0:
|
||||
|
@ -1183,7 +1338,7 @@ void ExecuteInstruction()
|
|||
}
|
||||
break;
|
||||
|
||||
// everything else is reserved/invalid
|
||||
// everything else is reserved/invalid
|
||||
default:
|
||||
{
|
||||
RaiseException(Exception::RI);
|
||||
|
@ -1192,117 +1347,71 @@ void ExecuteInstruction()
|
|||
}
|
||||
}
|
||||
|
||||
void ExecuteCop0Instruction()
|
||||
template<PGXPMode pgxp_mode>
|
||||
static void ExecuteImpl()
|
||||
{
|
||||
const Instruction inst = g_state.current_instruction;
|
||||
|
||||
if (inst.cop.IsCommonInstruction())
|
||||
g_state.frame_done = false;
|
||||
while (!g_state.frame_done)
|
||||
{
|
||||
switch (inst.cop.CommonOp())
|
||||
TimingEvents::UpdateCPUDowncount();
|
||||
|
||||
while (g_state.pending_ticks <= g_state.downcount)
|
||||
{
|
||||
case CopCommonInstruction::mfcn:
|
||||
{
|
||||
const std::optional<u32> value = ReadCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue()));
|
||||
if (value)
|
||||
WriteRegDelayed(inst.r.rt, value.value());
|
||||
else
|
||||
RaiseException(Exception::RI);
|
||||
}
|
||||
break;
|
||||
if (HasPendingInterrupt())
|
||||
DispatchInterrupt();
|
||||
|
||||
case CopCommonInstruction::mtcn:
|
||||
{
|
||||
WriteCop0Reg(static_cast<Cop0Reg>(inst.r.rd.GetValue()), ReadReg(inst.r.rt));
|
||||
}
|
||||
break;
|
||||
g_state.pending_ticks++;
|
||||
|
||||
default:
|
||||
Panic("Missing implementation");
|
||||
break;
|
||||
// now executing the instruction we previously fetched
|
||||
g_state.current_instruction.bits = g_state.next_instruction.bits;
|
||||
g_state.current_instruction_pc = g_state.regs.pc;
|
||||
g_state.current_instruction_in_branch_delay_slot = g_state.next_instruction_is_branch_delay_slot;
|
||||
g_state.current_instruction_was_branch_taken = g_state.branch_was_taken;
|
||||
g_state.next_instruction_is_branch_delay_slot = false;
|
||||
g_state.branch_was_taken = false;
|
||||
g_state.exception_raised = false;
|
||||
|
||||
// fetch the next instruction
|
||||
if (!FetchInstruction())
|
||||
continue;
|
||||
|
||||
#if 0 // GTE flag test debugging
|
||||
if (g_state.m_current_instruction_pc == 0x8002cdf4)
|
||||
{
|
||||
if (g_state.m_regs.v1 != g_state.m_regs.v0)
|
||||
printf("Got %08X Expected? %08X\n", g_state.m_regs.v1, g_state.m_regs.v0);
|
||||
}
|
||||
#endif
|
||||
|
||||
// execute the instruction we previously fetched
|
||||
ExecuteInstruction<pgxp_mode>();
|
||||
|
||||
// next load delay
|
||||
UpdateLoadDelay();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (inst.cop.Cop0Op())
|
||||
{
|
||||
case Cop0Instruction::rfe:
|
||||
{
|
||||
// restore mode
|
||||
g_state.cop0_regs.sr.mode_bits =
|
||||
(g_state.cop0_regs.sr.mode_bits & UINT32_C(0b110000)) | (g_state.cop0_regs.sr.mode_bits >> 2);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Panic("Missing implementation");
|
||||
break;
|
||||
}
|
||||
TimingEvents::RunEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void ExecuteCop2Instruction()
|
||||
void Execute()
|
||||
{
|
||||
const Instruction inst = g_state.current_instruction;
|
||||
|
||||
if (inst.cop.IsCommonInstruction())
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
{
|
||||
// TODO: Combine with cop0.
|
||||
switch (inst.cop.CommonOp())
|
||||
{
|
||||
case CopCommonInstruction::cfcn:
|
||||
{
|
||||
const u32 value = GTE::ReadRegister(static_cast<u32>(inst.r.rd.GetValue()) + 32);
|
||||
WriteRegDelayed(inst.r.rt, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
PGXP::CPU_CFC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::ctcn:
|
||||
{
|
||||
const u32 value = ReadReg(inst.r.rt);
|
||||
GTE::WriteRegister(static_cast<u32>(inst.r.rd.GetValue()) + 32, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
PGXP::CPU_CTC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::mfcn:
|
||||
{
|
||||
const u32 value = GTE::ReadRegister(static_cast<u32>(inst.r.rd.GetValue()));
|
||||
WriteRegDelayed(inst.r.rt, value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
PGXP::CPU_MFC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::mtcn:
|
||||
{
|
||||
const u32 value = ReadReg(inst.r.rt);
|
||||
GTE::WriteRegister(static_cast<u32>(inst.r.rd.GetValue()), value);
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
PGXP::CPU_MTC2(inst.bits, value, value);
|
||||
}
|
||||
break;
|
||||
|
||||
case CopCommonInstruction::bcnc:
|
||||
default:
|
||||
Panic("Missing implementation");
|
||||
break;
|
||||
}
|
||||
if (g_settings.gpu_pgxp_cpu)
|
||||
ExecuteImpl<PGXPMode::CPU>();
|
||||
else
|
||||
ExecuteImpl<PGXPMode::Memory>();
|
||||
}
|
||||
else
|
||||
{
|
||||
GTE::ExecuteInstruction(inst.bits);
|
||||
ExecuteImpl<PGXPMode::Disabled>();
|
||||
}
|
||||
}
|
||||
|
||||
namespace CodeCache {
|
||||
|
||||
template<PGXPMode pgxp_mode>
|
||||
void InterpretCachedBlock(const CodeBlock& block)
|
||||
{
|
||||
// set up the state so we've already fetched the instruction
|
||||
|
@ -1327,7 +1436,7 @@ void InterpretCachedBlock(const CodeBlock& block)
|
|||
g_state.regs.npc += 4;
|
||||
|
||||
// execute the instruction we previously fetched
|
||||
ExecuteInstruction();
|
||||
ExecuteInstruction<pgxp_mode>();
|
||||
|
||||
// next load delay
|
||||
UpdateLoadDelay();
|
||||
|
@ -1340,6 +1449,10 @@ void InterpretCachedBlock(const CodeBlock& block)
|
|||
g_state.next_instruction_is_branch_delay_slot = false;
|
||||
}
|
||||
|
||||
template void InterpretCachedBlock<PGXPMode::Disabled>(const CodeBlock& block);
|
||||
template void InterpretCachedBlock<PGXPMode::Memory>(const CodeBlock& block);
|
||||
template void InterpretCachedBlock<PGXPMode::CPU>(const CodeBlock& block);
|
||||
|
||||
void InterpretUncachedBlock()
|
||||
{
|
||||
Panic("Fixme with regards to re-fetching PC");
|
||||
|
@ -1365,7 +1478,7 @@ void InterpretUncachedBlock()
|
|||
break;
|
||||
|
||||
// execute the instruction we previously fetched
|
||||
ExecuteInstruction();
|
||||
ExecuteInstruction<PGXPMode::Disabled>();
|
||||
|
||||
// next load delay
|
||||
UpdateLoadDelay();
|
||||
|
@ -1387,7 +1500,13 @@ namespace Recompiler::Thunks {
|
|||
|
||||
bool InterpretInstruction()
|
||||
{
|
||||
ExecuteInstruction();
|
||||
ExecuteInstruction<PGXPMode::Disabled>();
|
||||
return g_state.exception_raised;
|
||||
}
|
||||
|
||||
bool InterpretInstructionPGXP()
|
||||
{
|
||||
ExecuteInstruction<PGXPMode::Memory>();
|
||||
return g_state.exception_raised;
|
||||
}
|
||||
|
||||
|
|
|
@ -1014,12 +1014,14 @@ bool CodeGenerator::Compile_Fallback(const CodeBlockInstruction& cbi)
|
|||
{
|
||||
// TODO: Use carry flag or something here too
|
||||
Value return_value = m_register_cache.AllocateScratch(RegSize_8);
|
||||
EmitFunctionCall(&return_value, &Thunks::InterpretInstruction);
|
||||
EmitFunctionCall(&return_value,
|
||||
g_settings.gpu_pgxp_enable ? &Thunks::InterpretInstructionPGXP : &Thunks::InterpretInstruction);
|
||||
EmitExceptionExitOnBool(return_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitFunctionCall(nullptr, &Thunks::InterpretInstruction);
|
||||
EmitFunctionCall(nullptr,
|
||||
g_settings.gpu_pgxp_enable ? &Thunks::InterpretInstructionPGXP : &Thunks::InterpretInstruction);
|
||||
}
|
||||
|
||||
m_current_instruction_in_branch_delay_slot_dirty = cbi.is_branch_instruction;
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Recompiler::Thunks {
|
|||
// TODO: Abuse carry flag or something else for exception
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool InterpretInstruction();
|
||||
bool InterpretInstructionPGXP();
|
||||
|
||||
// Memory access functions for the JIT - MSB is set on exception.
|
||||
u64 ReadMemoryByte(u32 address);
|
||||
|
|
|
@ -135,7 +135,6 @@ enum class InstructionFunct : u8
|
|||
or_ = 37,
|
||||
xor_ = 38,
|
||||
nor = 39,
|
||||
sh = 41,
|
||||
slt = 42,
|
||||
sltu = 43
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "common/log.h"
|
||||
#include "common/progress_callback.h"
|
||||
#include "common/string_util.h"
|
||||
#include "host_interface.h"
|
||||
#include "settings.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
@ -270,7 +271,12 @@ std::vector<std::string> GameList::ParseM3UFile(const char* path)
|
|||
const char* GameList::GetGameListCompatibilityRatingString(GameListCompatibilityRating rating)
|
||||
{
|
||||
static constexpr std::array<const char*, static_cast<size_t>(GameListCompatibilityRating::Count)> names = {
|
||||
{"Unknown", "Doesn't Boot", "Crashes In Intro", "Crashes In-Game", "Graphical/Audio Issues", "No Issues"}};
|
||||
{TRANSLATABLE("GameListCompatibilityRating", "Unknown"),
|
||||
TRANSLATABLE("GameListCompatibilityRating", "Doesn't Boot"),
|
||||
TRANSLATABLE("GameListCompatibilityRating", "Crashes In Intro"),
|
||||
TRANSLATABLE("GameListCompatibilityRating", "Crashes In-Game"),
|
||||
TRANSLATABLE("GameListCompatibilityRating", "Graphical/Audio Issues"),
|
||||
TRANSLATABLE("GameListCompatibilityRating", "No Issues")}};
|
||||
return (rating >= GameListCompatibilityRating::Unknown && rating < GameListCompatibilityRating::Count) ?
|
||||
names[static_cast<int>(rating)] :
|
||||
"";
|
||||
|
@ -446,6 +452,12 @@ bool GameList::GetGameListEntry(const std::string& path, GameListEntry* entry)
|
|||
entry->compatibility_rating = compatibility_entry->compatibility_rating;
|
||||
else
|
||||
Log_WarningPrintf("'%s' (%s) not found in compatibility list", entry->code.c_str(), entry->title.c_str());
|
||||
|
||||
if (!m_game_settings_load_tried)
|
||||
LoadGameSettings();
|
||||
const GameSettings::Entry* settings = m_game_settings.GetEntry(entry->code);
|
||||
if (settings)
|
||||
entry->settings = *settings;
|
||||
}
|
||||
|
||||
FILESYSTEM_STAT_DATA ffd;
|
||||
|
@ -577,6 +589,12 @@ bool GameList::LoadEntriesFromCache(ByteStream* stream)
|
|||
ge.type = static_cast<GameListEntryType>(type);
|
||||
ge.compatibility_rating = static_cast<GameListCompatibilityRating>(compatibility_rating);
|
||||
|
||||
if (!ge.settings.LoadFromStream(stream))
|
||||
{
|
||||
Log_WarningPrintf("Game list cache entry is corrupted (settings)");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto iter = m_cache_map.find(ge.path);
|
||||
if (iter != m_cache_map.end())
|
||||
iter->second = std::move(ge);
|
||||
|
@ -625,6 +643,7 @@ bool GameList::WriteEntryToCache(const GameListEntry* entry, ByteStream* stream)
|
|||
result &= WriteU8(stream, static_cast<u8>(entry->region));
|
||||
result &= WriteU8(stream, static_cast<u8>(entry->type));
|
||||
result &= WriteU8(stream, static_cast<u8>(entry->compatibility_rating));
|
||||
result &= entry->settings.SaveToStream(stream);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -853,6 +872,18 @@ const GameListEntry* GameList::GetEntryForPath(const char* path) const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
GameListEntry* GameList::GetMutableEntryForPath(const char* path)
|
||||
{
|
||||
const size_t path_length = std::strlen(path);
|
||||
for (GameListEntry& entry : m_entries)
|
||||
{
|
||||
if (entry.path.size() == path_length && StringUtil::Strcasecmp(entry.path.c_str(), path) == 0)
|
||||
return &entry;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const GameListDatabaseEntry* GameList::GetDatabaseEntryForCode(const std::string& code) const
|
||||
{
|
||||
if (!m_database_load_tried)
|
||||
|
@ -1272,3 +1303,46 @@ std::string GameList::ExportCompatibilityEntry(const GameListCompatibilityEntry*
|
|||
entry_elem->Accept(&printer);
|
||||
return std::string(printer.CStr(), printer.CStrSize());
|
||||
}
|
||||
|
||||
void GameList::LoadGameSettings()
|
||||
{
|
||||
if (m_game_settings_load_tried)
|
||||
return;
|
||||
|
||||
m_game_settings_load_tried = true;
|
||||
|
||||
if (!m_game_settings_filename.empty() && FileSystem::FileExists(m_user_game_settings_filename.c_str()))
|
||||
m_game_settings.Load(m_game_settings_filename.c_str());
|
||||
if (!m_user_game_settings_filename.empty() && FileSystem::FileExists(m_user_game_settings_filename.c_str()))
|
||||
m_game_settings.Load(m_user_game_settings_filename.c_str());
|
||||
}
|
||||
|
||||
const GameSettings::Entry* GameList::GetGameSettings(const std::string& filename, const std::string& game_code)
|
||||
{
|
||||
const GameListEntry* entry = GetMutableEntryForPath(filename.c_str());
|
||||
if (entry)
|
||||
return &entry->settings;
|
||||
|
||||
if (!m_game_settings_load_tried)
|
||||
LoadGameSettings();
|
||||
|
||||
return m_game_settings.GetEntry(game_code);
|
||||
}
|
||||
|
||||
void GameList::UpdateGameSettings(const std::string& filename, const std::string& game_code,
|
||||
const std::string& game_title, const GameSettings::Entry& new_entry,
|
||||
bool save_to_list /* = true */, bool save_to_user /* = true */)
|
||||
{
|
||||
GameListEntry* entry = GetMutableEntryForPath(filename.c_str());
|
||||
if (entry)
|
||||
{
|
||||
entry->settings = new_entry;
|
||||
RewriteCacheFile();
|
||||
}
|
||||
|
||||
if (save_to_list)
|
||||
{
|
||||
m_game_settings.SetEntry(game_code, game_title, new_entry,
|
||||
save_to_user ? m_user_game_settings_filename.c_str() : m_game_settings_filename.c_str());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
#include "game_settings.h"
|
||||
#include "types.h"
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
@ -48,6 +49,7 @@ struct GameListEntry
|
|||
DiscRegion region;
|
||||
GameListEntryType type;
|
||||
GameListCompatibilityRating compatibility_rating;
|
||||
GameSettings::Entry settings;
|
||||
};
|
||||
|
||||
struct GameListCompatibilityEntry
|
||||
|
@ -109,6 +111,8 @@ public:
|
|||
void SetCacheFilename(std::string filename) { m_cache_filename = std::move(filename); }
|
||||
void SetDatabaseFilename(std::string filename) { m_database_filename = std::move(filename); }
|
||||
void SetCompatibilityFilename(std::string filename) { m_compatibility_list_filename = std::move(filename); }
|
||||
void SetGameSettingsFilename(std::string filename) { m_game_settings_filename = std::move(filename); }
|
||||
void SetUserGameSettingsFilename(std::string filename) { m_user_game_settings_filename = std::move(filename); }
|
||||
void SetSearchDirectoriesFromSettings(SettingsInterface& si);
|
||||
|
||||
bool IsDatabasePresent() const;
|
||||
|
@ -120,11 +124,15 @@ public:
|
|||
|
||||
static std::string ExportCompatibilityEntry(const GameListCompatibilityEntry* entry);
|
||||
|
||||
const GameSettings::Entry* GetGameSettings(const std::string& filename, const std::string& game_code);
|
||||
void UpdateGameSettings(const std::string& filename, const std::string& game_code, const std::string& game_title,
|
||||
const GameSettings::Entry& new_entry, bool save_to_list = true, bool save_to_user = true);
|
||||
|
||||
private:
|
||||
enum : u32
|
||||
{
|
||||
GAME_LIST_CACHE_SIGNATURE = 0x45434C47,
|
||||
GAME_LIST_CACHE_VERSION = 5
|
||||
GAME_LIST_CACHE_VERSION = 6
|
||||
};
|
||||
|
||||
using DatabaseMap = std::unordered_map<std::string, GameListDatabaseEntry>;
|
||||
|
@ -140,6 +148,8 @@ private:
|
|||
class RedumpDatVisitor;
|
||||
class CompatibilityListVisitor;
|
||||
|
||||
GameListEntry* GetMutableEntryForPath(const char* path);
|
||||
|
||||
static bool GetExeListEntry(const char* path, GameListEntry* entry);
|
||||
bool GetM3UListEntry(const char* path, GameListEntry* entry);
|
||||
|
||||
|
@ -163,16 +173,22 @@ private:
|
|||
bool SaveCompatibilityDatabase();
|
||||
bool SaveCompatibilityDatabaseForEntry(const GameListCompatibilityEntry* entry);
|
||||
|
||||
void LoadGameSettings();
|
||||
|
||||
DatabaseMap m_database;
|
||||
EntryList m_entries;
|
||||
CacheMap m_cache_map;
|
||||
CompatibilityMap m_compatibility_list;
|
||||
GameSettings::Database m_game_settings;
|
||||
std::unique_ptr<ByteStream> m_cache_write_stream;
|
||||
|
||||
std::vector<DirectoryEntry> m_search_directories;
|
||||
std::string m_cache_filename;
|
||||
std::string m_database_filename;
|
||||
std::string m_compatibility_list_filename;
|
||||
std::string m_game_settings_filename;
|
||||
std::string m_user_game_settings_filename;
|
||||
bool m_database_load_tried = false;
|
||||
bool m_compatibility_list_load_tried = false;
|
||||
bool m_game_settings_load_tried = false;
|
||||
};
|
||||
|
|
429
src/core/game_settings.cpp
Normal file
429
src/core/game_settings.cpp
Normal file
|
@ -0,0 +1,429 @@
|
|||
#include "game_settings.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/byte_stream.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/string.h"
|
||||
#include "common/string_util.h"
|
||||
#include "host_interface.h"
|
||||
#include "settings.h"
|
||||
#include <array>
|
||||
#include <utility>
|
||||
Log_SetChannel(GameSettings);
|
||||
|
||||
#ifdef WIN32
|
||||
#include "common/windows_headers.h"
|
||||
#endif
|
||||
#include "SimpleIni.h"
|
||||
|
||||
namespace GameSettings {
|
||||
|
||||
std::array<std::pair<const char*, const char*>, static_cast<u32>(Trait::Count)> s_trait_names = {{
|
||||
{"ForceInterpreter", TRANSLATABLE("GameSettingsTrait", "Force Interpreter")},
|
||||
{"ForceSoftwareRenderer", TRANSLATABLE("GameSettingsTrait", "Force Software Renderer")},
|
||||
{"EnableInterlacing", TRANSLATABLE("GameSettingsTrait", "Enable Interlacing")},
|
||||
{"DisableTrueColor", TRANSLATABLE("GameSettingsTrait", "Disable True Color")},
|
||||
{"DisableUpscaling", TRANSLATABLE("GameSettingsTrait", "Disable Upscaling")},
|
||||
{"DisableScaledDithering", TRANSLATABLE("GameSettingsTrait", "Disable Scaled Dithering")},
|
||||
{"DisableWidescreen", TRANSLATABLE("GameSettingsTrait", "Disable Widescreen")},
|
||||
{"DisablePGXP", TRANSLATABLE("GameSettingsTrait", "Disable PGXP")},
|
||||
{"DisablePGXPCulling", TRANSLATABLE("GameSettingsTrait", "Disable PGXP Culling")},
|
||||
{"EnablePGXPVertexCache", TRANSLATABLE("GameSettingsTrait", "Enable PGXP Vertex Cache")},
|
||||
{"EnablePGXPCPUMode", TRANSLATABLE("GameSettingsTrait", "Enable PGXP CPU Mode")},
|
||||
{"ForceDigitalController", TRANSLATABLE("GameSettingsTrait", "Force Digital Controller")},
|
||||
{"EnableRecompilerMemoryExceptions", TRANSLATABLE("GameSettingsTrait", "Enable Recompiler Memory Exceptions")},
|
||||
}};
|
||||
|
||||
const char* GetTraitName(Trait trait)
|
||||
{
|
||||
DebugAssert(trait < Trait::Count);
|
||||
return s_trait_names[static_cast<u32>(trait)].first;
|
||||
}
|
||||
|
||||
const char* GetTraitDisplayName(Trait trait)
|
||||
{
|
||||
DebugAssert(trait < Trait::Count);
|
||||
return s_trait_names[static_cast<u32>(trait)].second;
|
||||
}
|
||||
|
||||
bool Entry::HasAnySettings() const
|
||||
{
|
||||
return traits.any() || display_active_start_offset >= 0 || display_active_end_offset > 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool ReadOptionalFromStream(ByteStream* stream, std::optional<T>* dest)
|
||||
{
|
||||
bool has_value;
|
||||
if (!stream->Read2(&has_value, sizeof(has_value)))
|
||||
return false;
|
||||
|
||||
if (!has_value)
|
||||
return true;
|
||||
|
||||
T value;
|
||||
if (!stream->Read2(&value, sizeof(T)))
|
||||
return false;
|
||||
|
||||
*dest = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool WriteOptionalToStream(ByteStream* stream, const std::optional<T>& src)
|
||||
{
|
||||
const bool has_value = src.has_value();
|
||||
if (!stream->Write2(&has_value, sizeof(has_value)))
|
||||
return false;
|
||||
|
||||
if (!has_value)
|
||||
return true;
|
||||
|
||||
return stream->Write2(&src.value(), sizeof(T));
|
||||
}
|
||||
|
||||
bool Entry::LoadFromStream(ByteStream* stream)
|
||||
{
|
||||
constexpr u32 num_bytes = (static_cast<u32>(Trait::Count) + 7) / 8;
|
||||
std::array<u8, num_bytes> bits;
|
||||
|
||||
if (!stream->Read2(bits.data(), num_bytes) || !ReadOptionalFromStream(stream, &display_active_start_offset) ||
|
||||
!ReadOptionalFromStream(stream, &display_active_end_offset) ||
|
||||
!ReadOptionalFromStream(stream, &display_crop_mode) || !ReadOptionalFromStream(stream, &display_aspect_ratio) ||
|
||||
!ReadOptionalFromStream(stream, &controller_1_type) || !ReadOptionalFromStream(stream, &controller_2_type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
traits.reset();
|
||||
for (u32 i = 0; i < static_cast<int>(Trait::Count); i++)
|
||||
{
|
||||
if ((bits[i / 8] & (1u << (i % 8))) != 0)
|
||||
AddTrait(static_cast<Trait>(i));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Entry::SaveToStream(ByteStream* stream) const
|
||||
{
|
||||
constexpr u32 num_bytes = (static_cast<u32>(Trait::Count) + 7) / 8;
|
||||
std::array<u8, num_bytes> bits;
|
||||
bits.fill(0);
|
||||
for (u32 i = 0; i < static_cast<int>(Trait::Count); i++)
|
||||
{
|
||||
if (HasTrait(static_cast<Trait>(i)))
|
||||
bits[i / 8] |= (1u << (i % 8));
|
||||
}
|
||||
|
||||
return stream->Write2(bits.data(), num_bytes) && WriteOptionalToStream(stream, display_active_start_offset) &&
|
||||
WriteOptionalToStream(stream, display_active_end_offset) && WriteOptionalToStream(stream, display_crop_mode) &&
|
||||
WriteOptionalToStream(stream, display_aspect_ratio) && WriteOptionalToStream(stream, controller_1_type) &&
|
||||
WriteOptionalToStream(stream, controller_2_type);
|
||||
}
|
||||
|
||||
static void ParseIniSection(Entry* entry, const char* section, const CSimpleIniA& ini)
|
||||
{
|
||||
for (u32 trait = 0; trait < static_cast<u32>(Trait::Count); trait++)
|
||||
{
|
||||
if (ini.GetBoolValue(section, s_trait_names[trait].first, false))
|
||||
entry->AddTrait(static_cast<Trait>(trait));
|
||||
}
|
||||
|
||||
long lvalue = ini.GetLongValue(section, "DisplayActiveStartOffset", 0);
|
||||
if (lvalue != 0)
|
||||
entry->display_active_start_offset = static_cast<s16>(lvalue);
|
||||
lvalue = ini.GetLongValue(section, "DisplayActiveEndOffset", 0);
|
||||
if (lvalue != 0)
|
||||
entry->display_active_end_offset = static_cast<s16>(lvalue);
|
||||
|
||||
const char* cvalue = ini.GetValue(section, "DisplayCropMode", nullptr);
|
||||
if (cvalue)
|
||||
entry->display_crop_mode = Settings::ParseDisplayCropMode(cvalue);
|
||||
cvalue = ini.GetValue(section, "DisplayAspectRatio", nullptr);
|
||||
if (cvalue)
|
||||
entry->display_aspect_ratio = Settings::ParseDisplayAspectRatio(cvalue);
|
||||
|
||||
cvalue = ini.GetValue(section, "Controller1Type", nullptr);
|
||||
if (cvalue)
|
||||
entry->controller_1_type = Settings::ParseControllerTypeName(cvalue);
|
||||
cvalue = ini.GetValue(section, "Controller2Type", nullptr);
|
||||
if (cvalue)
|
||||
entry->controller_2_type = Settings::ParseControllerTypeName(cvalue);
|
||||
|
||||
cvalue = ini.GetValue(section, "GPUWidescreenHack", nullptr);
|
||||
if (cvalue)
|
||||
entry->gpu_widescreen_hack = StringUtil::FromChars<bool>(cvalue);
|
||||
}
|
||||
|
||||
static void StoreIniSection(const Entry& entry, const char* section, CSimpleIniA& ini)
|
||||
{
|
||||
for (u32 trait = 0; trait < static_cast<u32>(Trait::Count); trait++)
|
||||
{
|
||||
if (entry.HasTrait(static_cast<Trait>(trait)))
|
||||
ini.SetBoolValue(section, s_trait_names[trait].first, true);
|
||||
}
|
||||
|
||||
if (entry.display_active_start_offset.has_value())
|
||||
ini.SetLongValue(section, "DisplayActiveStartOffset", entry.display_active_start_offset.value());
|
||||
|
||||
if (entry.display_active_end_offset.has_value())
|
||||
ini.SetLongValue(section, "DisplayActiveEndOffset", entry.display_active_end_offset.value());
|
||||
|
||||
if (entry.display_crop_mode.has_value())
|
||||
ini.SetValue(section, "DisplayCropMode", Settings::GetDisplayCropModeName(entry.display_crop_mode.value()));
|
||||
if (entry.display_aspect_ratio.has_value())
|
||||
{
|
||||
ini.SetValue(section, "DisplayAspectRatio",
|
||||
Settings::GetDisplayAspectRatioName(entry.display_aspect_ratio.value()));
|
||||
}
|
||||
|
||||
if (entry.controller_1_type.has_value())
|
||||
ini.SetValue(section, "Controller1Type", Settings::GetControllerTypeName(entry.controller_1_type.value()));
|
||||
if (entry.controller_2_type.has_value())
|
||||
ini.SetValue(section, "Controller2Type", Settings::GetControllerTypeName(entry.controller_2_type.value()));
|
||||
|
||||
if (entry.gpu_widescreen_hack.has_value())
|
||||
ini.SetValue(section, "GPUWidescreenHack", entry.gpu_widescreen_hack.value() ? "true" : "false");
|
||||
}
|
||||
|
||||
Database::Database() = default;
|
||||
|
||||
Database::~Database() = default;
|
||||
|
||||
const GameSettings::Entry* Database::GetEntry(const std::string& code) const
|
||||
{
|
||||
auto it = m_entries.find(code);
|
||||
return (it != m_entries.end()) ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
bool Database::Load(const char* path)
|
||||
{
|
||||
auto fp = FileSystem::OpenManagedCFile(path, "rb");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
CSimpleIniA ini;
|
||||
SI_Error err = ini.LoadFile(fp.get());
|
||||
if (err != SI_OK)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to parse game settings ini: %d", static_cast<int>(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::list<CSimpleIniA::Entry> sections;
|
||||
ini.GetAllSections(sections);
|
||||
for (const CSimpleIniA::Entry& section_entry : sections)
|
||||
{
|
||||
std::string code(section_entry.pItem);
|
||||
auto it = m_entries.find(code);
|
||||
if (it != m_entries.end())
|
||||
{
|
||||
ParseIniSection(&it->second, code.c_str(), ini);
|
||||
continue;
|
||||
}
|
||||
|
||||
Entry entry;
|
||||
ParseIniSection(&entry, code.c_str(), ini);
|
||||
m_entries.emplace(std::move(code), std::move(entry));
|
||||
}
|
||||
|
||||
Log_InfoPrintf("Loaded settings for %zu games from '%s'", sections.size(), path);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Database::SetEntry(const std::string& code, const std::string& name, const Entry& entry, const char* save_path)
|
||||
{
|
||||
if (save_path)
|
||||
{
|
||||
CSimpleIniA ini;
|
||||
if (FileSystem::FileExists(save_path))
|
||||
{
|
||||
auto fp = FileSystem::OpenManagedCFile(save_path, "rb");
|
||||
if (fp)
|
||||
{
|
||||
SI_Error err = ini.LoadFile(fp.get());
|
||||
if (err != SI_OK)
|
||||
Log_ErrorPrintf("Failed to parse game settings ini: %d. Contents will be lost.", static_cast<int>(err));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_ErrorPrintf("Failed to open existing settings ini: '%s'", save_path);
|
||||
}
|
||||
}
|
||||
|
||||
ini.Delete(code.c_str(), nullptr, false);
|
||||
ini.SetValue(code.c_str(), nullptr, nullptr, SmallString::FromFormat("# %s (%s)", code.c_str(), name.c_str()),
|
||||
false);
|
||||
StoreIniSection(entry, code.c_str(), ini);
|
||||
|
||||
const bool did_exist = FileSystem::FileExists(save_path);
|
||||
auto fp = FileSystem::OpenManagedCFile(save_path, "wb");
|
||||
if (fp)
|
||||
{
|
||||
// write file comment so simpleini doesn't get confused
|
||||
if (!did_exist)
|
||||
std::fputs("# DuckStation Game Settings\n\n", fp.get());
|
||||
|
||||
SI_Error err = ini.SaveFile(fp.get());
|
||||
if (err != SI_OK)
|
||||
Log_ErrorPrintf("Failed to save game settings ini: %d", static_cast<int>(err));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_ErrorPrintf("Failed to open settings ini for saving: '%s'", save_path);
|
||||
}
|
||||
}
|
||||
|
||||
auto it = m_entries.find(code);
|
||||
if (it != m_entries.end())
|
||||
it->second = entry;
|
||||
else
|
||||
m_entries.emplace(code, entry);
|
||||
}
|
||||
|
||||
void Entry::ApplySettings(bool display_osd_messages) const
|
||||
{
|
||||
constexpr float osd_duration = 10.0f;
|
||||
|
||||
if (display_active_start_offset.has_value())
|
||||
g_settings.display_active_start_offset = display_active_start_offset.value();
|
||||
if (display_active_end_offset.has_value())
|
||||
g_settings.display_active_end_offset = display_active_end_offset.value();
|
||||
|
||||
if (display_crop_mode.has_value())
|
||||
g_settings.display_crop_mode = display_crop_mode.value();
|
||||
if (display_aspect_ratio.has_value())
|
||||
g_settings.display_aspect_ratio = display_aspect_ratio.value();
|
||||
if (controller_1_type.has_value())
|
||||
g_settings.controller_types[0] = controller_1_type.value();
|
||||
if (controller_2_type.has_value())
|
||||
g_settings.controller_types[1] = controller_2_type.value();
|
||||
if (gpu_widescreen_hack.has_value())
|
||||
g_settings.gpu_widescreen_hack = gpu_widescreen_hack.value();
|
||||
|
||||
if (HasTrait(Trait::ForceInterpreter))
|
||||
{
|
||||
if (display_osd_messages && g_settings.cpu_execution_mode != CPUExecutionMode::Interpreter)
|
||||
g_host_interface->AddOSDMessage("CPU execution mode forced to interpreter by game settings.", osd_duration);
|
||||
|
||||
g_settings.cpu_execution_mode = CPUExecutionMode::Interpreter;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::ForceSoftwareRenderer))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_renderer != GPURenderer::Software)
|
||||
g_host_interface->AddOSDMessage("GPU renderer forced to software by game settings.", osd_duration);
|
||||
|
||||
g_settings.gpu_renderer = GPURenderer::Software;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::EnableInterlacing))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_disable_interlacing)
|
||||
g_host_interface->AddOSDMessage("Interlacing enabled by game settings.", osd_duration);
|
||||
|
||||
g_settings.gpu_disable_interlacing = false;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::DisableTrueColor))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_true_color)
|
||||
g_host_interface->AddOSDMessage("True color disabled by game settings.", osd_duration);
|
||||
|
||||
g_settings.gpu_true_color = false;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::DisableUpscaling))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_resolution_scale > 1)
|
||||
g_host_interface->AddOSDMessage("Upscaling disabled by game settings.", osd_duration);
|
||||
|
||||
g_settings.gpu_resolution_scale = 1;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::DisableScaledDithering))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_scaled_dithering)
|
||||
g_host_interface->AddOSDMessage("Scaled dithering disabled by game settings.", osd_duration);
|
||||
|
||||
g_settings.gpu_scaled_dithering = false;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::DisableWidescreen))
|
||||
{
|
||||
if (display_osd_messages &&
|
||||
(g_settings.display_aspect_ratio == DisplayAspectRatio::R16_9 || g_settings.gpu_widescreen_hack))
|
||||
{
|
||||
g_host_interface->AddOSDMessage("Widescreen disabled by game settings.", osd_duration);
|
||||
}
|
||||
|
||||
g_settings.display_aspect_ratio = DisplayAspectRatio::R4_3;
|
||||
g_settings.gpu_widescreen_hack = false;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::DisablePGXP))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_pgxp_enable)
|
||||
g_host_interface->AddOSDMessage("PGXP geometry correction disabled by game settings.", osd_duration);
|
||||
|
||||
g_settings.gpu_pgxp_enable = false;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::DisablePGXPCulling))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_pgxp_culling)
|
||||
g_host_interface->AddOSDMessage("PGXP culling disabled by game settings.", osd_duration);
|
||||
|
||||
g_settings.gpu_pgxp_culling = false;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::EnablePGXPVertexCache))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_pgxp_enable && !g_settings.gpu_pgxp_vertex_cache)
|
||||
g_host_interface->AddOSDMessage("PGXP vertex cache enabled by game settings.", osd_duration);
|
||||
|
||||
g_settings.gpu_pgxp_vertex_cache = true;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::EnablePGXPCPUMode))
|
||||
{
|
||||
if (display_osd_messages && g_settings.gpu_pgxp_enable && !g_settings.gpu_pgxp_cpu)
|
||||
g_host_interface->AddOSDMessage("PGXP CPU mode enabled by game settings.", osd_duration);
|
||||
|
||||
g_settings.gpu_pgxp_cpu = true;
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::ForceDigitalController))
|
||||
{
|
||||
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
|
||||
{
|
||||
if (g_settings.controller_types[i] != ControllerType::None &&
|
||||
g_settings.controller_types[i] != ControllerType::DigitalController)
|
||||
{
|
||||
if (display_osd_messages)
|
||||
{
|
||||
g_host_interface->AddFormattedOSDMessage(osd_duration, "Controller %u changed to digital by game settings.",
|
||||
i + 1u);
|
||||
}
|
||||
|
||||
g_settings.controller_types[i] = ControllerType::DigitalController;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (HasTrait(Trait::EnableRecompilerMemoryExceptions))
|
||||
{
|
||||
if (display_osd_messages && g_settings.cpu_execution_mode == CPUExecutionMode::Recompiler &&
|
||||
!g_settings.cpu_recompiler_memory_exceptions)
|
||||
{
|
||||
g_host_interface->AddOSDMessage("Recompiler memory exceptions enabled by game settings.", osd_duration);
|
||||
}
|
||||
|
||||
g_settings.cpu_recompiler_memory_exceptions = true;
|
||||
}
|
||||
|
||||
// TODO: Overscan settings.
|
||||
}
|
||||
|
||||
} // namespace GameSettings
|
74
src/core/game_settings.h
Normal file
74
src/core/game_settings.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
#pragma once
|
||||
#include "types.h"
|
||||
#include <bitset>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class ByteStream;
|
||||
|
||||
namespace GameSettings {
|
||||
enum class Trait : u32
|
||||
{
|
||||
ForceInterpreter,
|
||||
ForceSoftwareRenderer,
|
||||
EnableInterlacing,
|
||||
DisableTrueColor,
|
||||
DisableUpscaling,
|
||||
DisableScaledDithering,
|
||||
DisableWidescreen,
|
||||
DisablePGXP,
|
||||
DisablePGXPCulling,
|
||||
EnablePGXPVertexCache,
|
||||
EnablePGXPCPUMode,
|
||||
ForceDigitalController,
|
||||
EnableRecompilerMemoryExceptions,
|
||||
|
||||
Count
|
||||
};
|
||||
|
||||
const char* GetTraitName(Trait trait);
|
||||
const char* GetTraitDisplayName(Trait trait);
|
||||
|
||||
struct Entry
|
||||
{
|
||||
std::bitset<static_cast<int>(Trait::Count)> traits{};
|
||||
std::optional<s16> display_active_start_offset;
|
||||
std::optional<s16> display_active_end_offset;
|
||||
|
||||
// user settings
|
||||
std::optional<DisplayCropMode> display_crop_mode;
|
||||
std::optional<DisplayAspectRatio> display_aspect_ratio;
|
||||
std::optional<ControllerType> controller_1_type;
|
||||
std::optional<ControllerType> controller_2_type;
|
||||
std::optional<bool> gpu_widescreen_hack;
|
||||
|
||||
ALWAYS_INLINE bool HasTrait(Trait trait) const { return traits[static_cast<int>(trait)]; }
|
||||
ALWAYS_INLINE void AddTrait(Trait trait) { traits[static_cast<int>(trait)] = true; }
|
||||
ALWAYS_INLINE void RemoveTrait(Trait trait) { traits[static_cast<int>(trait)] = false; }
|
||||
ALWAYS_INLINE void SetTrait(Trait trait, bool enabled) { traits[static_cast<int>(trait)] = enabled; }
|
||||
|
||||
bool HasAnySettings() const;
|
||||
|
||||
bool LoadFromStream(ByteStream* stream);
|
||||
bool SaveToStream(ByteStream* stream) const;
|
||||
|
||||
void ApplySettings(bool display_osd_messages) const;
|
||||
};
|
||||
|
||||
class Database
|
||||
{
|
||||
public:
|
||||
Database();
|
||||
~Database();
|
||||
|
||||
const Entry* GetEntry(const std::string& code) const;
|
||||
void SetEntry(const std::string& code, const std::string& name, const Entry& entry, const char* save_path);
|
||||
|
||||
bool Load(const char* path);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, Entry> m_entries;
|
||||
};
|
||||
|
||||
}; // namespace GameSettings
|
|
@ -513,15 +513,15 @@ void GPU::UpdateCRTCDisplayParameters()
|
|||
switch (crop_mode)
|
||||
{
|
||||
case DisplayCropMode::None:
|
||||
cs.horizontal_active_start = 487;
|
||||
cs.horizontal_active_end = 3282;
|
||||
cs.horizontal_active_start = static_cast<u16>(std::max<int>(0, 487 + g_settings.display_active_start_offset));
|
||||
cs.horizontal_active_end = static_cast<u16>(std::max<int>(0, 3282 + g_settings.display_active_end_offset));
|
||||
cs.vertical_active_start = 20;
|
||||
cs.vertical_active_end = 308;
|
||||
break;
|
||||
|
||||
case DisplayCropMode::Overscan:
|
||||
cs.horizontal_active_start = 628;
|
||||
cs.horizontal_active_end = 3188;
|
||||
cs.horizontal_active_start = static_cast<u16>(std::max<int>(0, 628 + g_settings.display_active_start_offset));
|
||||
cs.horizontal_active_end = static_cast<u16>(std::max<int>(0, 3188 + g_settings.display_active_end_offset));
|
||||
cs.vertical_active_start = 30;
|
||||
cs.vertical_active_end = 298;
|
||||
break;
|
||||
|
@ -540,15 +540,15 @@ void GPU::UpdateCRTCDisplayParameters()
|
|||
switch (crop_mode)
|
||||
{
|
||||
case DisplayCropMode::None:
|
||||
cs.horizontal_active_start = 488;
|
||||
cs.horizontal_active_end = 3288;
|
||||
cs.horizontal_active_start = static_cast<u16>(std::max<int>(0, 488 + g_settings.display_active_start_offset));
|
||||
cs.horizontal_active_end = static_cast<u16>(std::max<int>(0, 3288 + g_settings.display_active_end_offset));
|
||||
cs.vertical_active_start = 16;
|
||||
cs.vertical_active_end = 256;
|
||||
break;
|
||||
|
||||
case DisplayCropMode::Overscan:
|
||||
cs.horizontal_active_start = 608;
|
||||
cs.horizontal_active_end = 3168;
|
||||
cs.horizontal_active_start = static_cast<u16>(std::max<int>(0, 608 + g_settings.display_active_start_offset));
|
||||
cs.horizontal_active_end = static_cast<u16>(std::max<int>(0, 3168 + g_settings.display_active_end_offset));
|
||||
cs.vertical_active_start = 24;
|
||||
cs.vertical_active_end = 248;
|
||||
break;
|
||||
|
@ -759,9 +759,15 @@ void GPU::CRTCTickEvent(TickCount ticks)
|
|||
// start the new frame
|
||||
m_crtc_state.current_scanline = 0;
|
||||
if (m_GPUSTAT.vertical_interlace)
|
||||
{
|
||||
m_crtc_state.interlaced_field ^= 1u;
|
||||
m_GPUSTAT.interlaced_field = m_crtc_state.interlaced_field;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_crtc_state.interlaced_field = 0;
|
||||
m_GPUSTAT.interlaced_field = 0u; // new GPU = 1, old GPU = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -489,7 +489,7 @@ protected:
|
|||
BitField<u32, bool, 10, 1> draw_to_displayed_field;
|
||||
BitField<u32, bool, 11, 1> set_mask_while_drawing;
|
||||
BitField<u32, bool, 12, 1> check_mask_before_draw;
|
||||
BitField<u32, bool, 13, 1> interlaced_field;
|
||||
BitField<u32, u8, 13, 1> interlaced_field;
|
||||
BitField<u32, bool, 14, 1> reverse_flag;
|
||||
BitField<u32, bool, 15, 1> texture_disable;
|
||||
BitField<u32, u8, 16, 1> horizontal_resolution_2;
|
||||
|
|
|
@ -588,7 +588,7 @@ void GPU_HW_OpenGL::ClearDisplay()
|
|||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
m_vram_texture.BindFramebuffer(GL_DRAW_FRAMEBUFFER);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_vram_fbo_id);
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::UpdateDisplay()
|
||||
|
|
|
@ -625,7 +625,7 @@ static void RTPS(const s16 V[3], u8 shift, bool lm, bool last)
|
|||
if (g_settings.gpu_pgxp_enable)
|
||||
{
|
||||
// this can potentially use increased precision on Z
|
||||
const float precise_z = std::max<float>((float)REGS.H / 2.f, (float)REGS.SZ3);
|
||||
const float precise_z = std::max<float>((float)REGS.H / 2.f, (float)z / 4096.0f);
|
||||
const float precise_h_div_sz = (float)REGS.H / precise_z;
|
||||
const float fofx = ((float)REGS.OFX / (float)(1 << 16));
|
||||
const float fofy = ((float)REGS.OFY / (float)(1 << 16));
|
||||
|
|
|
@ -114,7 +114,7 @@ void HostInterface::ResetSystem()
|
|||
{
|
||||
System::Reset();
|
||||
System::ResetPerformanceCounters();
|
||||
AddOSDMessage("System reset.");
|
||||
AddOSDMessage(TranslateStdString("OSDMessage", "System reset."));
|
||||
}
|
||||
|
||||
void HostInterface::PowerOffSystem()
|
||||
|
@ -284,13 +284,13 @@ bool HostInterface::LoadState(const char* filename)
|
|||
if (!stream)
|
||||
return false;
|
||||
|
||||
AddFormattedOSDMessage(5.0f, "Loading state from '%s'...", filename);
|
||||
AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "Loading state from '%s'..."), filename);
|
||||
|
||||
if (!System::IsShutdown())
|
||||
{
|
||||
if (!System::LoadState(stream.get()))
|
||||
{
|
||||
ReportFormattedError("Loading state from '%s' failed. Resetting.", filename);
|
||||
ReportFormattedError(TranslateString("OSDMessage", "Loading state from '%s' failed. Resetting."), filename);
|
||||
ResetSystem();
|
||||
return false;
|
||||
}
|
||||
|
@ -318,12 +318,12 @@ bool HostInterface::SaveState(const char* filename)
|
|||
const bool result = System::SaveState(stream.get());
|
||||
if (!result)
|
||||
{
|
||||
ReportFormattedError("Saving state to '%s' failed.", filename);
|
||||
ReportFormattedError(TranslateString("OSDMessage", "Saving state to '%s' failed."), filename);
|
||||
stream->Discard();
|
||||
}
|
||||
else
|
||||
{
|
||||
AddFormattedOSDMessage(5.0f, "State saved to '%s'.", filename);
|
||||
AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "State saved to '%s'."), filename);
|
||||
stream->Commit();
|
||||
}
|
||||
|
||||
|
@ -358,6 +358,7 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
|
|||
si.SetBoolValue("Main", "SaveStateOnExit", true);
|
||||
si.SetBoolValue("Main", "ConfirmPowerOff", true);
|
||||
si.SetBoolValue("Main", "LoadDevicesFromSaveStates", false);
|
||||
si.SetBoolValue("Main", "ApplyGameSettings", true);
|
||||
|
||||
si.SetStringValue("CPU", "ExecutionMode", Settings::GetCPUExecutionModeName(Settings::DEFAULT_CPU_EXECUTION_MODE));
|
||||
si.SetBoolValue("CPU", "RecompilerMemoryExceptions", false);
|
||||
|
@ -375,8 +376,11 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
|
|||
si.SetBoolValue("GPU", "PGXPCulling", true);
|
||||
si.SetBoolValue("GPU", "PGXPTextureCorrection", true);
|
||||
si.SetBoolValue("GPU", "PGXPVertexCache", false);
|
||||
si.SetBoolValue("GPU", "PGXPCPU", false);
|
||||
|
||||
si.SetStringValue("Display", "CropMode", Settings::GetDisplayCropModeName(Settings::DEFAULT_DISPLAY_CROP_MODE));
|
||||
si.SetIntValue("Display", "OverscanActiveStartOffset", 0);
|
||||
si.SetIntValue("Display", "OverscanActiveEndOffset", 0);
|
||||
si.SetStringValue("Display", "AspectRatio",
|
||||
Settings::GetDisplayAspectRatioName(Settings::DEFAULT_DISPLAY_ASPECT_RATIO));
|
||||
si.SetBoolValue("Display", "LinearFiltering", true);
|
||||
|
@ -440,6 +444,32 @@ void HostInterface::LoadSettings(SettingsInterface& si)
|
|||
g_settings.Load(si);
|
||||
}
|
||||
|
||||
void HostInterface::FixIncompatibleSettings(bool display_osd_messages)
|
||||
{
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
{
|
||||
if (g_settings.gpu_renderer == GPURenderer::Software)
|
||||
{
|
||||
if (display_osd_messages)
|
||||
{
|
||||
AddOSDMessage(TranslateStdString("OSDMessage", "PGXP is incompatible with the software renderer, disabling PGXP."), 10.0f);
|
||||
}
|
||||
g_settings.gpu_pgxp_enable = false;
|
||||
}
|
||||
else if (g_settings.gpu_pgxp_cpu && g_settings.cpu_execution_mode == CPUExecutionMode::Recompiler)
|
||||
{
|
||||
if (display_osd_messages)
|
||||
{
|
||||
AddOSDMessage(
|
||||
TranslateStdString("OSDMessage",
|
||||
"PGXP CPU mode is incompatible with the recompiler, using Cached Interpreter instead."),
|
||||
10.0f);
|
||||
}
|
||||
g_settings.cpu_execution_mode = CPUExecutionMode::CachedInterpreter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HostInterface::SaveSettings(SettingsInterface& si)
|
||||
{
|
||||
g_settings.Save(si);
|
||||
|
@ -447,7 +477,7 @@ void HostInterface::SaveSettings(SettingsInterface& si)
|
|||
|
||||
void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
||||
{
|
||||
if (!System::IsShutdown())
|
||||
if (System::IsValid())
|
||||
{
|
||||
if (g_settings.gpu_renderer != old_settings.gpu_renderer ||
|
||||
g_settings.gpu_use_debug_device != old_settings.gpu_use_debug_device)
|
||||
|
@ -502,7 +532,9 @@ void HostInterface::CheckForSettingsChanges(const Settings& old_settings)
|
|||
g_settings.gpu_force_ntsc_timings != old_settings.gpu_force_ntsc_timings ||
|
||||
g_settings.display_crop_mode != old_settings.display_crop_mode ||
|
||||
g_settings.display_aspect_ratio != old_settings.display_aspect_ratio ||
|
||||
g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable)
|
||||
g_settings.gpu_pgxp_enable != old_settings.gpu_pgxp_enable ||
|
||||
g_settings.display_active_start_offset != old_settings.display_active_start_offset ||
|
||||
g_settings.display_active_end_offset != old_settings.display_active_end_offset)
|
||||
{
|
||||
g_gpu->UpdateSettings();
|
||||
}
|
||||
|
@ -658,6 +690,16 @@ float HostInterface::GetFloatSettingValue(const char* section, const char* key,
|
|||
return float_value.value_or(default_value);
|
||||
}
|
||||
|
||||
TinyString HostInterface::TranslateString(const char* context, const char* str) const
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
std::string HostInterface::TranslateStdString(const char* context, const char* str) const
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
void HostInterface::ToggleSoftwareRendering()
|
||||
{
|
||||
if (System::IsShutdown() || g_settings.gpu_renderer == GPURenderer::Software)
|
||||
|
|
|
@ -109,6 +109,10 @@ public:
|
|||
/// Returns a float setting from the configuration.
|
||||
virtual float GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f);
|
||||
|
||||
/// Translates a string to the current language.
|
||||
virtual TinyString TranslateString(const char* context, const char* str) const;
|
||||
virtual std::string TranslateStdString(const char* context, const char* str) const;
|
||||
|
||||
/// Loads the BIOS image for the specified region.
|
||||
std::optional<std::vector<u8>> GetBIOSImage(ConsoleRegion region);
|
||||
|
||||
|
@ -134,6 +138,9 @@ protected:
|
|||
/// Saves current settings variables to ini.
|
||||
virtual void SaveSettings(SettingsInterface& si);
|
||||
|
||||
/// Checks and fixes up any incompatible settings.
|
||||
virtual void FixIncompatibleSettings(bool display_osd_messages);
|
||||
|
||||
/// Checks for settings changes, std::move() the old settings away for comparing beforehand.
|
||||
virtual void CheckForSettingsChanges(const Settings& old_settings);
|
||||
|
||||
|
@ -161,4 +168,6 @@ protected:
|
|||
std::string m_user_directory;
|
||||
};
|
||||
|
||||
#define TRANSLATABLE(context, str) str
|
||||
|
||||
extern HostInterface* g_host_interface;
|
||||
|
|
1040
src/core/pgxp.cpp
1040
src/core/pgxp.cpp
File diff suppressed because it is too large
Load diff
|
@ -41,7 +41,8 @@ void CPU_CTC2(u32 instr, u32 rdVal, u32 rtVal); // copy GPR reg to GTE ctrl reg
|
|||
void CPU_LWC2(u32 instr, u32 rtVal, u32 addr); // copy memory to GTE reg
|
||||
void CPU_SWC2(u32 instr, u32 rtVal, u32 addr); // copy GTE reg to memory
|
||||
|
||||
bool GetPreciseVertex(u32 addr, u32 value, int x, int y, int xOffs, int yOffs, float* out_x, float* out_y, float* out_w);
|
||||
bool GetPreciseVertex(u32 addr, u32 value, int x, int y, int xOffs, int yOffs, float* out_x, float* out_y,
|
||||
float* out_w);
|
||||
|
||||
// -- CPU functions
|
||||
void CPU_LW(u32 instr, u32 rtVal, u32 addr);
|
||||
|
@ -51,4 +52,56 @@ void CPU_SB(u32 instr, u8 rtVal, u32 addr);
|
|||
void CPU_SH(u32 instr, u16 rtVal, u32 addr);
|
||||
void CPU_SW(u32 instr, u32 rtVal, u32 addr);
|
||||
|
||||
// Arithmetic with immediate value
|
||||
void CPU_ADDI(u32 instr, u32 rtVal, u32 rsVal);
|
||||
void CPU_ADDIU(u32 instr, u32 rtVal, u32 rsVal);
|
||||
void CPU_ANDI(u32 instr, u32 rtVal, u32 rsVal);
|
||||
void CPU_ORI(u32 instr, u32 rtVal, u32 rsVal);
|
||||
void CPU_XORI(u32 instr, u32 rtVal, u32 rsVal);
|
||||
void CPU_SLTI(u32 instr, u32 rtVal, u32 rsVal);
|
||||
void CPU_SLTIU(u32 instr, u32 rtVal, u32 rsVal);
|
||||
|
||||
// Load Upper
|
||||
void CPU_LUI(u32 instr, u32 rtVal);
|
||||
|
||||
// Register Arithmetic
|
||||
void CPU_ADD(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_ADDU(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_SUB(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_SUBU(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_AND_(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_OR_(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_XOR_(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_NOR(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_SLT(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_SLTU(u32 instr, u32 rdVal, u32 rsVal, u32 rtVal);
|
||||
|
||||
// Register mult/div
|
||||
void CPU_MULT(u32 instr, u32 hiVal, u32 loVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_MULTU(u32 instr, u32 hiVal, u32 loVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_DIV(u32 instr, u32 hiVal, u32 loVal, u32 rsVal, u32 rtVal);
|
||||
void CPU_DIVU(u32 instr, u32 hiVal, u32 loVal, u32 rsVal, u32 rtVal);
|
||||
|
||||
// Shift operations (sa)
|
||||
void CPU_SLL(u32 instr, u32 rdVal, u32 rtVal);
|
||||
void CPU_SRL(u32 instr, u32 rdVal, u32 rtVal);
|
||||
void CPU_SRA(u32 instr, u32 rdVal, u32 rtVal);
|
||||
|
||||
// Shift operations variable
|
||||
void CPU_SLLV(u32 instr, u32 rdVal, u32 rtVal, u32 rsVal);
|
||||
void CPU_SRLV(u32 instr, u32 rdVal, u32 rtVal, u32 rsVal);
|
||||
void CPU_SRAV(u32 instr, u32 rdVal, u32 rtVal, u32 rsVal);
|
||||
|
||||
// Move registers
|
||||
void CPU_MFHI(u32 instr, u32 rdVal, u32 hiVal);
|
||||
void CPU_MTHI(u32 instr, u32 hiVal, u32 rdVal);
|
||||
void CPU_MFLO(u32 instr, u32 rdVal, u32 loVal);
|
||||
void CPU_MTLO(u32 instr, u32 loVal, u32 rdVal);
|
||||
|
||||
// CP0 Data transfer tracking
|
||||
void CPU_MFC0(u32 instr, u32 rtVal, u32 rdVal);
|
||||
void CPU_MTC0(u32 instr, u32 rdVal, u32 rtVal);
|
||||
void CPU_CFC0(u32 instr, u32 rtVal, u32 rdVal);
|
||||
void CPU_CTC0(u32 instr, u32 rdVal, u32 rtVal);
|
||||
|
||||
} // namespace PGXP
|
|
@ -84,6 +84,7 @@ void Settings::Load(SettingsInterface& si)
|
|||
save_state_on_exit = si.GetBoolValue("Main", "SaveStateOnExit", true);
|
||||
confim_power_off = si.GetBoolValue("Main", "ConfirmPowerOff", true);
|
||||
load_devices_from_save_states = si.GetBoolValue("Main", "LoadDevicesFromSaveStates", false);
|
||||
apply_game_settings = si.GetBoolValue("Main", "ApplyGameSettings", true);
|
||||
|
||||
cpu_execution_mode =
|
||||
ParseCPUExecutionMode(
|
||||
|
@ -106,6 +107,7 @@ void Settings::Load(SettingsInterface& si)
|
|||
gpu_pgxp_culling = si.GetBoolValue("GPU", "PGXPCulling", true);
|
||||
gpu_pgxp_texture_correction = si.GetBoolValue("GPU", "PGXPTextureCorrection", true);
|
||||
gpu_pgxp_vertex_cache = si.GetBoolValue("GPU", "PGXPVertexCache", false);
|
||||
gpu_pgxp_cpu = si.GetBoolValue("GPU", "PGXPCPU", false);
|
||||
|
||||
display_crop_mode =
|
||||
ParseDisplayCropMode(
|
||||
|
@ -115,6 +117,8 @@ void Settings::Load(SettingsInterface& si)
|
|||
ParseDisplayAspectRatio(
|
||||
si.GetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(DEFAULT_DISPLAY_ASPECT_RATIO)).c_str())
|
||||
.value_or(DEFAULT_DISPLAY_ASPECT_RATIO);
|
||||
display_active_start_offset = static_cast<s16>(si.GetIntValue("Display", "ActiveStartOffset", 0));
|
||||
display_active_end_offset = static_cast<s16>(si.GetIntValue("Display", "ActiveEndOffset", 0));
|
||||
display_linear_filtering = si.GetBoolValue("Display", "LinearFiltering", true);
|
||||
display_integer_scaling = si.GetBoolValue("Display", "IntegerScaling", false);
|
||||
display_show_osd_messages = si.GetBoolValue("Display", "ShowOSDMessages", true);
|
||||
|
@ -197,6 +201,7 @@ void Settings::Save(SettingsInterface& si) const
|
|||
si.SetBoolValue("Main", "SaveStateOnExit", save_state_on_exit);
|
||||
si.SetBoolValue("Main", "ConfirmPowerOff", confim_power_off);
|
||||
si.SetBoolValue("Main", "LoadDevicesFromSaveStates", load_devices_from_save_states);
|
||||
si.SetBoolValue("Main", "ApplyGameSettings", apply_game_settings);
|
||||
|
||||
si.SetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(cpu_execution_mode));
|
||||
si.SetBoolValue("CPU", "RecompilerMemoryExceptions", cpu_recompiler_memory_exceptions);
|
||||
|
@ -215,8 +220,11 @@ void Settings::Save(SettingsInterface& si) const
|
|||
si.SetBoolValue("GPU", "PGXPCulling", gpu_pgxp_culling);
|
||||
si.SetBoolValue("GPU", "PGXPTextureCorrection", gpu_pgxp_texture_correction);
|
||||
si.SetBoolValue("GPU", "PGXPVertexCache", gpu_pgxp_vertex_cache);
|
||||
si.SetBoolValue("GPU", "PGXPCPU", gpu_pgxp_cpu);
|
||||
|
||||
si.SetStringValue("Display", "CropMode", GetDisplayCropModeName(display_crop_mode));
|
||||
si.SetIntValue("Display", "ActiveStartOffset", display_active_start_offset);
|
||||
si.SetIntValue("Display", "ActiveEndOffset", display_active_end_offset);
|
||||
si.SetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(display_aspect_ratio));
|
||||
si.SetBoolValue("Display", "LinearFiltering", display_linear_filtering);
|
||||
si.SetBoolValue("Display", "IntegerScaling", display_integer_scaling);
|
||||
|
@ -283,7 +291,10 @@ void Settings::Save(SettingsInterface& si) const
|
|||
static std::array<const char*, LOGLEVEL_COUNT> s_log_level_names = {
|
||||
{"None", "Error", "Warning", "Perf", "Success", "Info", "Dev", "Profile", "Debug", "Trace"}};
|
||||
static std::array<const char*, LOGLEVEL_COUNT> s_log_level_display_names = {
|
||||
{"None", "Error", "Warning", "Performance", "Success", "Information", "Developer", "Profile", "Debug", "Trace"}};
|
||||
{TRANSLATABLE("LogLevel", "None"), TRANSLATABLE("LogLevel", "Error"), TRANSLATABLE("LogLevel", "Warning"),
|
||||
TRANSLATABLE("LogLevel", "Performance"), TRANSLATABLE("LogLevel", "Success"),
|
||||
TRANSLATABLE("LogLevel", "Information"), TRANSLATABLE("LogLevel", "Developer"), TRANSLATABLE("LogLevel", "Profile"),
|
||||
TRANSLATABLE("LogLevel", "Debug"), TRANSLATABLE("LogLevel", "Trace")}};
|
||||
|
||||
std::optional<LOGLEVEL> Settings::ParseLogLevelName(const char* str)
|
||||
{
|
||||
|
@ -311,7 +322,8 @@ const char* Settings::GetLogLevelDisplayName(LOGLEVEL level)
|
|||
|
||||
static std::array<const char*, 4> s_console_region_names = {{"Auto", "NTSC-J", "NTSC-U", "PAL"}};
|
||||
static std::array<const char*, 4> s_console_region_display_names = {
|
||||
{"Auto-Detect", "NTSC-J (Japan)", "NTSC-U (US)", "PAL (Europe, Australia)"}};
|
||||
{TRANSLATABLE("ConsoleRegion", "Auto-Detect"), TRANSLATABLE("ConsoleRegion", "NTSC-J (Japan)"),
|
||||
TRANSLATABLE("ConsoleRegion", "NTSC-U (US)"), TRANSLATABLE("ConsoleRegion", "PAL (Europe, Australia)")}};
|
||||
|
||||
std::optional<ConsoleRegion> Settings::ParseConsoleRegionName(const char* str)
|
||||
{
|
||||
|
@ -339,7 +351,8 @@ const char* Settings::GetConsoleRegionDisplayName(ConsoleRegion region)
|
|||
|
||||
static std::array<const char*, 4> s_disc_region_names = {{"NTSC-J", "NTSC-U", "PAL", "Other"}};
|
||||
static std::array<const char*, 4> s_disc_region_display_names = {
|
||||
{"NTSC-J (Japan)", "NTSC-U (US)", "PAL (Europe, Australia)", "Other"}};
|
||||
{TRANSLATABLE("DiscRegion", "NTSC-J (Japan)"), TRANSLATABLE("DiscRegion", "NTSC-U (US)"),
|
||||
TRANSLATABLE("DiscRegion", "PAL (Europe, Australia)"), TRANSLATABLE("DiscRegion", "Other")}};
|
||||
|
||||
std::optional<DiscRegion> Settings::ParseDiscRegionName(const char* str)
|
||||
{
|
||||
|
@ -367,7 +380,8 @@ const char* Settings::GetDiscRegionDisplayName(DiscRegion region)
|
|||
|
||||
static std::array<const char*, 3> s_cpu_execution_mode_names = {{"Interpreter", "CachedInterpreter", "Recompiler"}};
|
||||
static std::array<const char*, 3> s_cpu_execution_mode_display_names = {
|
||||
{"Intepreter (Slowest)", "Cached Interpreter (Faster)", "Recompiler (Fastest)"}};
|
||||
{TRANSLATABLE("CPUExecutionMode", "Intepreter (Slowest)"), TRANSLATABLE("CPUExecutionMode", "Cached Interpreter (Faster)"),
|
||||
TRANSLATABLE("CPUExecutionMode", "Recompiler (Fastest)")}};
|
||||
|
||||
std::optional<CPUExecutionMode> Settings::ParseCPUExecutionMode(const char* str)
|
||||
{
|
||||
|
@ -400,9 +414,10 @@ static std::array<const char*, 4> s_gpu_renderer_names = {{
|
|||
"Vulkan", "OpenGL", "Software"}};
|
||||
static std::array<const char*, 4> s_gpu_renderer_display_names = {{
|
||||
#ifdef WIN32
|
||||
"Hardware (D3D11)",
|
||||
TRANSLATABLE("GPURenderer", "Hardware (D3D11)"),
|
||||
#endif
|
||||
"Hardware (Vulkan)", "Hardware (OpenGL)", "Software"}};
|
||||
TRANSLATABLE("GPURenderer", "Hardware (Vulkan)"), TRANSLATABLE("GPURenderer", "Hardware (OpenGL)"),
|
||||
TRANSLATABLE("GPURenderer", "Software")}};
|
||||
|
||||
std::optional<GPURenderer> Settings::ParseRendererName(const char* str)
|
||||
{
|
||||
|
@ -429,7 +444,9 @@ const char* Settings::GetRendererDisplayName(GPURenderer renderer)
|
|||
}
|
||||
|
||||
static std::array<const char*, 3> s_display_crop_mode_names = {{"None", "Overscan", "Borders"}};
|
||||
static std::array<const char*, 3> s_display_crop_mode_display_names = {{"None", "Only Overscan Area", "All Borders"}};
|
||||
static std::array<const char*, 3> s_display_crop_mode_display_names = {{TRANSLATABLE("DisplayCropMode", "None"),
|
||||
TRANSLATABLE("DisplayCropMode", "Only Overscan Area"),
|
||||
TRANSLATABLE("DisplayCropMode", "All Borders")}};
|
||||
|
||||
std::optional<DisplayCropMode> Settings::ParseDisplayCropMode(const char* str)
|
||||
{
|
||||
|
@ -485,7 +502,8 @@ float Settings::GetDisplayAspectRatioValue(DisplayAspectRatio ar)
|
|||
}
|
||||
|
||||
static std::array<const char*, 3> s_audio_backend_names = {{"Null", "Cubeb", "SDL"}};
|
||||
static std::array<const char*, 3> s_audio_backend_display_names = {{"Null (No Output)", "Cubeb", "SDL"}};
|
||||
static std::array<const char*, 3> s_audio_backend_display_names = {
|
||||
{TRANSLATABLE("AudioBackend", "Null (No Output)"), TRANSLATABLE("AudioBackend", "Cubeb"), TRANSLATABLE("AudioBackend", "SDL")}};
|
||||
|
||||
std::optional<AudioBackend> Settings::ParseAudioBackend(const char* str)
|
||||
{
|
||||
|
@ -514,7 +532,9 @@ const char* Settings::GetAudioBackendDisplayName(AudioBackend backend)
|
|||
static std::array<const char*, 6> s_controller_type_names = {
|
||||
{"None", "DigitalController", "AnalogController", "NamcoGunCon", "PlayStationMouse", "NeGcon"}};
|
||||
static std::array<const char*, 6> s_controller_display_names = {
|
||||
{"None", "Digital Controller", "Analog Controller (DualShock)", "Namco GunCon", "PlayStation Mouse", "NeGcon"}};
|
||||
{TRANSLATABLE("ControllerType", "None"), TRANSLATABLE("ControllerType", "Digital Controller"),
|
||||
TRANSLATABLE("ControllerType", "Analog Controller (DualShock)"), TRANSLATABLE("ControllerType", "Namco GunCon"),
|
||||
TRANSLATABLE("ControllerType", "PlayStation Mouse"), TRANSLATABLE("ControllerType", "NeGcon")}};
|
||||
|
||||
std::optional<ControllerType> Settings::ParseControllerTypeName(const char* str)
|
||||
{
|
||||
|
@ -541,9 +561,10 @@ const char* Settings::GetControllerTypeDisplayName(ControllerType type)
|
|||
}
|
||||
|
||||
static std::array<const char*, 4> s_memory_card_type_names = {{"None", "Shared", "PerGame", "PerGameTitle"}};
|
||||
static std::array<const char*, 4> s_memory_card_type_display_names = {{"No Memory Card", "Shared Between All Games",
|
||||
"Separate Card Per Game (Game Code)",
|
||||
"Separate Card Per Game (Game Title)"}};
|
||||
static std::array<const char*, 4> s_memory_card_type_display_names = {
|
||||
{TRANSLATABLE("MemoryCardType", "No Memory Card"), TRANSLATABLE("MemoryCardType", "Shared Between All Games"),
|
||||
TRANSLATABLE("MemoryCardType", "Separate Card Per Game (Game Code)"),
|
||||
TRANSLATABLE("MemoryCardType", "Separate Card Per Game (Game Title)")}};
|
||||
|
||||
std::optional<MemoryCardType> Settings::ParseMemoryCardTypeName(const char* str)
|
||||
{
|
||||
|
|
|
@ -78,6 +78,7 @@ struct Settings
|
|||
bool save_state_on_exit = true;
|
||||
bool confim_power_off = true;
|
||||
bool load_devices_from_save_states = false;
|
||||
bool apply_game_settings = true;
|
||||
|
||||
GPURenderer gpu_renderer = GPURenderer::Software;
|
||||
std::string gpu_adapter;
|
||||
|
@ -93,7 +94,10 @@ struct Settings
|
|||
bool gpu_pgxp_culling = true;
|
||||
bool gpu_pgxp_texture_correction = true;
|
||||
bool gpu_pgxp_vertex_cache = false;
|
||||
bool gpu_pgxp_cpu = false;
|
||||
DisplayCropMode display_crop_mode = DisplayCropMode::None;
|
||||
s16 display_active_start_offset = 0;
|
||||
s16 display_active_end_offset = 0;
|
||||
DisplayAspectRatio display_aspect_ratio = DisplayAspectRatio::R4_3;
|
||||
bool display_linear_filtering = true;
|
||||
bool display_integer_scaling = false;
|
||||
|
@ -157,6 +161,11 @@ struct Settings
|
|||
ALWAYS_INLINE bool IsUsingRecompiler() const { return (cpu_execution_mode == CPUExecutionMode::Recompiler); }
|
||||
ALWAYS_INLINE bool IsUsingSoftwareRenderer() const { return (gpu_renderer == GPURenderer::Software); }
|
||||
|
||||
ALWAYS_INLINE PGXPMode GetPGXPMode()
|
||||
{
|
||||
return gpu_pgxp_enable ? (gpu_pgxp_cpu ? PGXPMode::CPU : PGXPMode::Memory) : PGXPMode::Disabled;
|
||||
}
|
||||
|
||||
bool HasAnyPerGameMemoryCards() const;
|
||||
|
||||
enum : u32
|
||||
|
|
|
@ -131,7 +131,7 @@ bool IsShutdown()
|
|||
|
||||
bool IsValid()
|
||||
{
|
||||
return s_state != State::Shutdown;
|
||||
return s_state != State::Shutdown && s_state != State::Starting;
|
||||
}
|
||||
|
||||
ConsoleRegion GetRegion()
|
||||
|
@ -384,6 +384,9 @@ bool Boot(const SystemBootParameters& params)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Notify change of disc.
|
||||
UpdateRunningGame(media ? media->GetFileName().c_str() : params.filename.c_str(), media.get());
|
||||
|
||||
// Component setup.
|
||||
if (!Initialize(params.force_software_renderer))
|
||||
{
|
||||
|
@ -391,8 +394,6 @@ bool Boot(const SystemBootParameters& params)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Notify change of disc.
|
||||
UpdateRunningGame(params.filename.c_str(), media.get());
|
||||
UpdateControllers();
|
||||
UpdateMemoryCards();
|
||||
Reset();
|
||||
|
@ -649,8 +650,10 @@ bool DoLoadState(ByteStream* state, bool force_software_renderer)
|
|||
|
||||
if (header.version != SAVE_STATE_VERSION)
|
||||
{
|
||||
g_host_interface->ReportFormattedError("Save state is incompatible: expecting version %u but state is version %u.",
|
||||
SAVE_STATE_VERSION, header.version);
|
||||
g_host_interface->ReportFormattedError(
|
||||
g_host_interface->TranslateString("System",
|
||||
"Save state is incompatible: expecting version %u but state is version %u."),
|
||||
SAVE_STATE_VERSION, header.version);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -671,8 +674,9 @@ bool DoLoadState(ByteStream* state, bool force_software_renderer)
|
|||
media = OpenCDImage(media_filename.c_str(), false);
|
||||
if (!media)
|
||||
{
|
||||
g_host_interface->ReportFormattedError("Failed to open CD image from save state: '%s'.",
|
||||
media_filename.c_str());
|
||||
g_host_interface->ReportFormattedError(
|
||||
g_host_interface->TranslateString("System", "Failed to open CD image from save state: '%s'."),
|
||||
media_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1172,10 +1176,12 @@ void UpdateMemoryCards()
|
|||
{
|
||||
if (s_running_game_code.empty())
|
||||
{
|
||||
g_host_interface->AddFormattedOSDMessage(5.0f,
|
||||
"Per-game memory card cannot be used for slot %u as the running "
|
||||
"game has no code. Using shared card instead.",
|
||||
i + 1u);
|
||||
g_host_interface->AddFormattedOSDMessage(
|
||||
5.0f,
|
||||
g_host_interface->TranslateString("System",
|
||||
"Per-game memory card cannot be used for slot %u as the running "
|
||||
"game has no code. Using shared card instead."),
|
||||
i + 1u);
|
||||
card = MemoryCard::Open(g_host_interface->GetSharedMemoryCardPath(i));
|
||||
}
|
||||
else
|
||||
|
@ -1194,10 +1200,12 @@ void UpdateMemoryCards()
|
|||
}
|
||||
else if (s_running_game_title.empty())
|
||||
{
|
||||
g_host_interface->AddFormattedOSDMessage(5.0f,
|
||||
"Per-game memory card cannot be used for slot %u as the running "
|
||||
"game has no title. Using shared card instead.",
|
||||
i + 1u);
|
||||
g_host_interface->AddFormattedOSDMessage(
|
||||
5.0f,
|
||||
g_host_interface->TranslateString("System",
|
||||
"Per-game memory card cannot be used for slot %u as the running "
|
||||
"game has no title. Using shared card instead."),
|
||||
i + 1u);
|
||||
card = MemoryCard::Open(g_host_interface->GetSharedMemoryCardPath(i));
|
||||
}
|
||||
else
|
||||
|
@ -1211,8 +1219,10 @@ void UpdateMemoryCards()
|
|||
{
|
||||
if (g_settings.memory_card_paths[i].empty())
|
||||
{
|
||||
g_host_interface->AddFormattedOSDMessage(10.0f, "Memory card path for slot %u is missing, using default.",
|
||||
i + 1u);
|
||||
g_host_interface->AddFormattedOSDMessage(
|
||||
10.0f,
|
||||
g_host_interface->TranslateString("System", "Memory card path for slot %u is missing, using default."),
|
||||
i + 1u);
|
||||
card = MemoryCard::Open(g_host_interface->GetSharedMemoryCardPath(i));
|
||||
}
|
||||
else
|
||||
|
@ -1255,7 +1265,8 @@ bool InsertMedia(const char* path)
|
|||
|
||||
if (g_settings.HasAnyPerGameMemoryCards())
|
||||
{
|
||||
g_host_interface->AddOSDMessage("Game changed, reloading memory cards.", 2.0f);
|
||||
g_host_interface->AddOSDMessage(
|
||||
g_host_interface->TranslateStdString("System", "Game changed, reloading memory cards."), 10.0f);
|
||||
UpdateMemoryCards();
|
||||
}
|
||||
|
||||
|
@ -1269,6 +1280,9 @@ void RemoveMedia()
|
|||
|
||||
void UpdateRunningGame(const char* path, CDImage* image)
|
||||
{
|
||||
if (s_running_game_path == path)
|
||||
return;
|
||||
|
||||
s_running_game_path.clear();
|
||||
s_running_game_code.clear();
|
||||
s_running_game_title.clear();
|
||||
|
|
|
@ -48,6 +48,13 @@ enum class CPUExecutionMode : u8
|
|||
Count
|
||||
};
|
||||
|
||||
enum class PGXPMode : u8
|
||||
{
|
||||
Disabled,
|
||||
Memory,
|
||||
CPU
|
||||
};
|
||||
|
||||
enum class GPURenderer : u8
|
||||
{
|
||||
#ifdef WIN32
|
||||
|
|
|
@ -63,6 +63,23 @@ LibretroHostInterface::~LibretroHostInterface()
|
|||
}
|
||||
}
|
||||
|
||||
void LibretroHostInterface::InitInterfaces()
|
||||
{
|
||||
SetCoreOptions();
|
||||
InitDiskControlInterface();
|
||||
|
||||
if (!m_interfaces_initialized)
|
||||
{
|
||||
InitLogging();
|
||||
InitRumbleInterface();
|
||||
|
||||
unsigned dummy = 0;
|
||||
m_supports_input_bitmasks = g_retro_environment_callback(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, &dummy);
|
||||
|
||||
m_interfaces_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void LibretroHostInterface::InitLogging()
|
||||
{
|
||||
if (s_libretro_log_callback_registered)
|
||||
|
@ -84,6 +101,7 @@ bool LibretroHostInterface::Initialize()
|
|||
return false;
|
||||
|
||||
LoadSettings();
|
||||
FixIncompatibleSettings(true);
|
||||
UpdateLogging();
|
||||
return true;
|
||||
}
|
||||
|
@ -352,7 +370,7 @@ void LibretroHostInterface::OnSystemDestroyed()
|
|||
m_using_hardware_renderer = false;
|
||||
}
|
||||
|
||||
static std::array<retro_core_option_definition, 30> s_option_definitions = {{
|
||||
static std::array<retro_core_option_definition, 31> s_option_definitions = {{
|
||||
{"duckstation_Console.Region",
|
||||
"Console Region",
|
||||
"Determines which region/hardware to emulate. Auto-Detect will use the region of the disc inserted.",
|
||||
|
@ -483,6 +501,12 @@ static std::array<retro_core_option_definition, 30> s_option_definitions = {{
|
|||
"Uses screen coordinates as a fallback when tracking vertices through memory fails. May improve PGXP compatibility.",
|
||||
{{"true", "Enabled"}, {"false", "Disabled"}},
|
||||
"false"},
|
||||
{"duckstation_GPU.PGXPCPU",
|
||||
"PGXP CPU Mode",
|
||||
"Tries to track vertex manipulation through the CPU. Some games require this option for PGXP to be effective. "
|
||||
"Very slow, and incompatible with the recompiler.",
|
||||
{{"true", "Enabled"}, {"false", "Disabled"}},
|
||||
"false"},
|
||||
{"duckstation_Display.CropMode",
|
||||
"Crop Mode",
|
||||
"Changes how much of the image is cropped. Some games display garbage in the overscan area which is typically "
|
||||
|
@ -494,9 +518,9 @@ static std::array<retro_core_option_definition, 30> s_option_definitions = {{
|
|||
"Sets the core-provided aspect ratio.",
|
||||
{{"4:3", "4:3"}, {"16:9", "16:9"}, {"2:1", "2:1 (VRAM 1:1)"}, {"1:1", "1:1"}},
|
||||
"4:3"},
|
||||
{"duckstation_MemoryCards.LoadFromSaveStates",
|
||||
"Load Memory Cards From Save States",
|
||||
"Sets whether the contents of memory cards will be loaded when a save state is loaded.",
|
||||
{"duckstation_Main.LoadDevicesFromSaveStates",
|
||||
"Load Devices From Save States",
|
||||
"Sets whether the contents of devices and memory cards will be loaded when a save state is loaded.",
|
||||
{{"true", "Enabled"}, {"false", "Disabled"}},
|
||||
"false"},
|
||||
{"duckstation_MemoryCards.Card1Type",
|
||||
|
@ -520,7 +544,7 @@ static std::array<retro_core_option_definition, 30> s_option_definitions = {{
|
|||
"When using a playlist (m3u) and per-game (title) memory cards, a single memory card "
|
||||
"will be used for all discs. If unchecked, a separate card will be used for each disc.",
|
||||
{{"true", "Enabled"}, {"false", "Disabled"}},
|
||||
"false"},
|
||||
"true"},
|
||||
{"duckstation_Controller1.Type",
|
||||
"Controller 1 Type",
|
||||
"Sets the type of controller for Slot 1.",
|
||||
|
@ -590,7 +614,7 @@ bool LibretroHostInterface::HasCoreVariablesChanged()
|
|||
void LibretroHostInterface::LoadSettings()
|
||||
{
|
||||
LibretroSettingsInterface si;
|
||||
g_settings.Load(si);
|
||||
HostInterface::LoadSettings(si);
|
||||
|
||||
// Assume BIOS files are located in system directory.
|
||||
const char* system_directory = nullptr;
|
||||
|
@ -608,6 +632,7 @@ void LibretroHostInterface::UpdateSettings()
|
|||
{
|
||||
Settings old_settings(std::move(g_settings));
|
||||
LoadSettings();
|
||||
FixIncompatibleSettings(false);
|
||||
|
||||
if (g_settings.gpu_resolution_scale != old_settings.gpu_resolution_scale &&
|
||||
g_settings.gpu_renderer != GPURenderer::Software)
|
||||
|
@ -650,6 +675,11 @@ void LibretroHostInterface::CheckForSettingsChanges(const Settings& old_settings
|
|||
UpdateLogging();
|
||||
}
|
||||
|
||||
void LibretroHostInterface::InitRumbleInterface()
|
||||
{
|
||||
m_rumble_interface_valid = g_retro_environment_callback(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &m_rumble_interface);
|
||||
}
|
||||
|
||||
void LibretroHostInterface::UpdateControllers()
|
||||
{
|
||||
g_retro_input_poll_callback();
|
||||
|
@ -698,10 +728,19 @@ void LibretroHostInterface::UpdateControllersDigitalController(u32 index)
|
|||
{DigitalController::Button::R1, RETRO_DEVICE_ID_JOYPAD_R},
|
||||
{DigitalController::Button::R2, RETRO_DEVICE_ID_JOYPAD_R2}}};
|
||||
|
||||
for (const auto& it : mapping)
|
||||
if (m_supports_input_bitmasks)
|
||||
{
|
||||
const int16_t state = g_retro_input_state_callback(index, RETRO_DEVICE_JOYPAD, 0, it.second);
|
||||
controller->SetButtonState(it.first, state != 0);
|
||||
const u16 active = g_retro_input_state_callback(index, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
|
||||
for (const auto& it : mapping)
|
||||
controller->SetButtonState(it.first, (active & (static_cast<u16>(1u) << it.second)) != 0u);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& it : mapping)
|
||||
{
|
||||
const int16_t state = g_retro_input_state_callback(index, RETRO_DEVICE_JOYPAD, 0, it.second);
|
||||
controller->SetButtonState(it.first, state != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -734,10 +773,19 @@ void LibretroHostInterface::UpdateControllersAnalogController(u32 index)
|
|||
{AnalogController::Axis::RightX, {RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X}},
|
||||
{AnalogController::Axis::RightY, {RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y}}}};
|
||||
|
||||
for (const auto& it : button_mapping)
|
||||
if (m_supports_input_bitmasks)
|
||||
{
|
||||
const int16_t state = g_retro_input_state_callback(index, RETRO_DEVICE_JOYPAD, 0, it.second);
|
||||
controller->SetButtonState(it.first, state != 0);
|
||||
const u16 active = g_retro_input_state_callback(index, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
|
||||
for (const auto& it : button_mapping)
|
||||
controller->SetButtonState(it.first, (active & (static_cast<u16>(1u) << it.second)) != 0u);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& it : button_mapping)
|
||||
{
|
||||
const int16_t state = g_retro_input_state_callback(index, RETRO_DEVICE_JOYPAD, 0, it.second);
|
||||
controller->SetButtonState(it.first, state != 0);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& it : axis_mapping)
|
||||
|
@ -745,6 +793,14 @@ void LibretroHostInterface::UpdateControllersAnalogController(u32 index)
|
|||
const int16_t state = g_retro_input_state_callback(index, RETRO_DEVICE_ANALOG, it.second.first, it.second.second);
|
||||
controller->SetAxisState(static_cast<s32>(it.first), std::clamp(static_cast<float>(state) / 32767.0f, -1.0f, 1.0f));
|
||||
}
|
||||
|
||||
if (m_rumble_interface_valid)
|
||||
{
|
||||
const u16 strong = static_cast<u16>(static_cast<u32>(controller->GetVibrationMotorStrength(0) * 65565.0f));
|
||||
const u16 weak = static_cast<u16>(static_cast<u32>(controller->GetVibrationMotorStrength(1) * 65565.0f));
|
||||
m_rumble_interface.set_rumble_state(index, RETRO_RUMBLE_STRONG, strong);
|
||||
m_rumble_interface.set_rumble_state(index, RETRO_RUMBLE_WEAK, weak);
|
||||
}
|
||||
}
|
||||
|
||||
static std::optional<GPURenderer> RetroHwContextToRenderer(retro_hw_context_type type)
|
||||
|
|
|
@ -11,10 +11,7 @@ public:
|
|||
LibretroHostInterface();
|
||||
~LibretroHostInterface() override;
|
||||
|
||||
static void InitLogging();
|
||||
static bool SetCoreOptions();
|
||||
static bool HasCoreVariablesChanged();
|
||||
static void InitDiskControlInterface();
|
||||
void InitInterfaces();
|
||||
|
||||
ALWAYS_INLINE u32 GetResolutionScale() const { return g_settings.gpu_resolution_scale; }
|
||||
|
||||
|
@ -51,6 +48,12 @@ protected:
|
|||
void CheckForSettingsChanges(const Settings& old_settings) override;
|
||||
|
||||
private:
|
||||
bool SetCoreOptions();
|
||||
bool HasCoreVariablesChanged();
|
||||
void InitLogging();
|
||||
void InitDiskControlInterface();
|
||||
void InitRumbleInterface();
|
||||
|
||||
void LoadSettings();
|
||||
void UpdateSettings();
|
||||
void UpdateControllers();
|
||||
|
@ -86,6 +89,11 @@ private:
|
|||
bool m_hw_render_callback_valid = false;
|
||||
bool m_using_hardware_renderer = false;
|
||||
std::optional<u32> m_next_disc_index;
|
||||
|
||||
retro_rumble_interface m_rumble_interface = {};
|
||||
bool m_rumble_interface_valid = false;
|
||||
bool m_supports_input_bitmasks = false;
|
||||
bool m_interfaces_initialized = false;
|
||||
};
|
||||
|
||||
extern LibretroHostInterface g_libretro_host_interface;
|
||||
|
|
|
@ -122,12 +122,7 @@ RETRO_API size_t retro_get_memory_size(unsigned id)
|
|||
RETRO_API void retro_set_environment(retro_environment_t f)
|
||||
{
|
||||
g_retro_environment_callback = f;
|
||||
|
||||
if (!g_libretro_host_interface.SetCoreOptions())
|
||||
Log_WarningPrintf("Failed to set core options, settings will not be changeable.");
|
||||
|
||||
g_libretro_host_interface.InitLogging();
|
||||
g_libretro_host_interface.InitDiskControlInterface();
|
||||
g_libretro_host_interface.InitInterfaces();
|
||||
}
|
||||
|
||||
RETRO_API void retro_set_video_refresh(retro_video_refresh_t f)
|
||||
|
|
|
@ -68,6 +68,7 @@ set(TS_FILES
|
|||
translations/duckstation-qt_de.ts
|
||||
translations/duckstation-qt_es.ts
|
||||
translations/duckstation-qt_he.ts
|
||||
translations/duckstation-qt_it.ts
|
||||
translations/duckstation-qt_pt-br.ts
|
||||
translations/duckstation-qt_pt-pt.ts
|
||||
translations/duckstation-qt_zh-cn.ts
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "advancedsettingswidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "settingsdialog.h"
|
||||
#include "settingwidgetbinder.h"
|
||||
|
||||
|
@ -8,7 +9,7 @@ AdvancedSettingsWidget::AdvancedSettingsWidget(QtHostInterface* host_interface,
|
|||
m_ui.setupUi(this);
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(LOGLEVEL_COUNT); i++)
|
||||
m_ui.logLevel->addItem(tr(Settings::GetLogLevelDisplayName(static_cast<LOGLEVEL>(i))));
|
||||
m_ui.logLevel->addItem(qApp->translate("LogLevel", Settings::GetLogLevelDisplayName(static_cast<LOGLEVEL>(i))));
|
||||
|
||||
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.logLevel, "Logging", "LogLevel",
|
||||
&Settings::ParseLogLevelName, &Settings::GetLogLevelName,
|
||||
|
@ -27,9 +28,12 @@ AdvancedSettingsWidget::AdvancedSettingsWidget(QtHostInterface* host_interface,
|
|||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.cpuRecompilerMemoryExceptions, "CPU",
|
||||
"RecompilerMemoryExceptions", false);
|
||||
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.showDebugMenu, "Main", "ShowDebugMenu");
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.gpuUseDebugDevice, "GPU", "UseDebugDevice");
|
||||
|
||||
connect(m_ui.resetToDefaultButton, &QPushButton::clicked, this, &AdvancedSettingsWidget::onResetToDefaultClicked);
|
||||
connect(m_ui.showDebugMenu, &QCheckBox::toggled, m_host_interface->getMainWindow(),
|
||||
&MainWindow::updateDebugMenuVisibility, Qt::QueuedConnection);
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.gpuUseDebugDevice, tr("Use Debug Host GPU Device"), tr("Unchecked"),
|
||||
tr("Enables the usage of debug devices and shaders for rendering APIs which support them. "
|
||||
|
|
|
@ -207,7 +207,14 @@
|
|||
<string>System Settings</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="showDebugMenu">
|
||||
<property name="text">
|
||||
<string>Show Debug Menu</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="gpuUseDebugDevice">
|
||||
<property name="text">
|
||||
<string>Use Debug Host GPU Device</string>
|
||||
|
|
|
@ -11,7 +11,10 @@ AudioSettingsWidget::AudioSettingsWidget(QtHostInterface* host_interface, QWidge
|
|||
m_ui.setupUi(this);
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(AudioBackend::Count); i++)
|
||||
m_ui.audioBackend->addItem(tr(Settings::GetAudioBackendDisplayName(static_cast<AudioBackend>(i))));
|
||||
{
|
||||
m_ui.audioBackend->addItem(
|
||||
qApp->translate("AudioBackend", Settings::GetAudioBackendDisplayName(static_cast<AudioBackend>(i))));
|
||||
}
|
||||
|
||||
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.audioBackend, "Audio", "Backend",
|
||||
&Settings::ParseAudioBackend, &Settings::GetAudioBackendName,
|
||||
|
|
|
@ -39,6 +39,7 @@ Log_SetChannel(AutoUpdaterDialog);
|
|||
static constexpr char LATEST_TAG_URL[] = "https://api.github.com/repos/stenzek/duckstation/tags";
|
||||
static constexpr char LATEST_RELEASE_URL[] =
|
||||
"https://api.github.com/repos/stenzek/duckstation/releases/tags/" SCM_RELEASE_TAG;
|
||||
static constexpr char CHANGES_URL[] = "https://api.github.com/repos/stenzek/duckstation/compare/%s..." SCM_RELEASE_TAG;
|
||||
static constexpr char UPDATE_ASSET_FILENAME[] = SCM_RELEASE_ASSET;
|
||||
|
||||
#endif
|
||||
|
@ -196,10 +197,11 @@ void AutoUpdaterDialog::getLatestReleaseComplete(QNetworkReply* reply)
|
|||
m_download_url = asset_obj["browser_download_url"].toString();
|
||||
if (!m_download_url.isEmpty())
|
||||
{
|
||||
m_ui.currentVersion->setText(tr("Current Version: %1 (%2)").arg(g_scm_hash_str).arg(__TIMESTAMP__));
|
||||
m_ui.currentVersion->setText(tr("Current Version: %1 (%2)").arg(g_scm_hash_str).arg(g_scm_date_str));
|
||||
m_ui.newVersion->setText(
|
||||
tr("New Version: %1 (%2)").arg(m_latest_sha).arg(doc_object["published_at"].toString()));
|
||||
m_ui.updateNotes->setText(doc_object["body"].toString());
|
||||
m_ui.updateNotes->setText(tr("Loading..."));
|
||||
queueGetChanges();
|
||||
exec();
|
||||
emit updateCheckCompleted();
|
||||
return;
|
||||
|
@ -223,6 +225,68 @@ void AutoUpdaterDialog::getLatestReleaseComplete(QNetworkReply* reply)
|
|||
#endif
|
||||
}
|
||||
|
||||
void AutoUpdaterDialog::queueGetChanges()
|
||||
{
|
||||
#ifdef AUTO_UPDATER_SUPPORTED
|
||||
connect(m_network_access_mgr, &QNetworkAccessManager::finished, this, &AutoUpdaterDialog::getChangesComplete);
|
||||
|
||||
const std::string url_string(StringUtil::StdStringFromFormat(CHANGES_URL, g_scm_hash_str));
|
||||
QUrl url(QUrl::fromEncoded(QByteArray(url_string.c_str(), static_cast<int>(url_string.size()))));
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
m_network_access_mgr->get(request);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AutoUpdaterDialog::getChangesComplete(QNetworkReply* reply)
|
||||
{
|
||||
#ifdef AUTO_UPDATER_SUPPORTED
|
||||
m_network_access_mgr->disconnect(this);
|
||||
reply->deleteLater();
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError)
|
||||
{
|
||||
const QByteArray reply_json(reply->readAll());
|
||||
QJsonParseError parse_error;
|
||||
QJsonDocument doc(QJsonDocument::fromJson(reply_json, &parse_error));
|
||||
if (doc.isObject())
|
||||
{
|
||||
const QJsonObject doc_object(doc.object());
|
||||
|
||||
QString changes_html = QStringLiteral("<ul>");
|
||||
|
||||
const QJsonArray commits(doc_object["commits"].toArray());
|
||||
for (const QJsonValue& commit : commits)
|
||||
{
|
||||
const QJsonObject commit_obj(commit["commit"].toObject());
|
||||
|
||||
QString message = commit_obj["message"].toString();
|
||||
QString author = commit_obj["author"].toObject()["name"].toString();
|
||||
const int first_line_terminator = message.indexOf('\n');
|
||||
if (first_line_terminator >= 0)
|
||||
message.remove(first_line_terminator, message.size() - first_line_terminator);
|
||||
if (!message.isEmpty())
|
||||
changes_html +=
|
||||
QStringLiteral("<li>%1 <i>(%2)</i></li>").arg(message.toHtmlEscaped()).arg(author.toHtmlEscaped());
|
||||
}
|
||||
|
||||
changes_html += "</ul>";
|
||||
m_ui.updateNotes->setText(changes_html);
|
||||
}
|
||||
else
|
||||
{
|
||||
reportError("Change list JSON is not an object");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
reportError("Failed to download change list: %d", static_cast<int>(reply->error()));
|
||||
}
|
||||
#endif
|
||||
|
||||
m_ui.downloadAndInstall->setEnabled(true);
|
||||
}
|
||||
|
||||
void AutoUpdaterDialog::downloadUpdateClicked()
|
||||
{
|
||||
QUrl url(m_download_url);
|
||||
|
|
|
@ -28,6 +28,9 @@ private Q_SLOTS:
|
|||
void getLatestTagComplete(QNetworkReply* reply);
|
||||
void getLatestReleaseComplete(QNetworkReply* reply);
|
||||
|
||||
void queueGetChanges();
|
||||
void getChangesComplete(QNetworkReply* reply);
|
||||
|
||||
void downloadUpdateClicked();
|
||||
void skipThisUpdateClicked();
|
||||
void remindMeLaterClicked();
|
||||
|
|
|
@ -82,6 +82,9 @@
|
|||
<property name="text">
|
||||
<string>Download and Install...</string>
|
||||
</property>
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
|
|
@ -11,10 +11,16 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QW
|
|||
m_ui.setupUi(this);
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(ConsoleRegion::Count); i++)
|
||||
m_ui.region->addItem(tr(Settings::GetConsoleRegionDisplayName(static_cast<ConsoleRegion>(i))));
|
||||
{
|
||||
m_ui.region->addItem(
|
||||
qApp->translate("ConsoleRegion", Settings::GetConsoleRegionDisplayName(static_cast<ConsoleRegion>(i))));
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(CPUExecutionMode::Count); i++)
|
||||
m_ui.cpuExecutionMode->addItem(tr(Settings::GetCPUExecutionModeDisplayName(static_cast<CPUExecutionMode>(i))));
|
||||
{
|
||||
m_ui.cpuExecutionMode->addItem(
|
||||
qApp->translate("CPUExecutionMode", Settings::GetCPUExecutionModeDisplayName(static_cast<CPUExecutionMode>(i))));
|
||||
}
|
||||
|
||||
SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.region, "Console", "Region",
|
||||
&Settings::ParseConsoleRegionName, &Settings::GetConsoleRegionName,
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <QtCore/QSignalBlocker>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtGui/QCursor>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QKeyEvent>
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QInputDialog>
|
||||
|
@ -48,7 +49,7 @@ void ControllerSettingsWidget::onProfileLoaded()
|
|||
ControllerType ctype = Settings::ParseControllerTypeName(
|
||||
m_host_interface
|
||||
->GetStringSettingValue(QStringLiteral("Controller%1").arg(i + 1).toStdString().c_str(),
|
||||
QStringLiteral("Type").toStdString().c_str())
|
||||
QStringLiteral("Type").toStdString().c_str())
|
||||
.c_str())
|
||||
.value_or(ControllerType::None);
|
||||
|
||||
|
@ -86,7 +87,7 @@ void ControllerSettingsWidget::createPortSettingsUi(int index, PortSettingsUI* u
|
|||
for (int i = 0; i < static_cast<int>(ControllerType::Count); i++)
|
||||
{
|
||||
ui->controller_type->addItem(
|
||||
QString::fromUtf8(Settings::GetControllerTypeDisplayName(static_cast<ControllerType>(i))));
|
||||
qApp->translate("ControllerType", Settings::GetControllerTypeDisplayName(static_cast<ControllerType>(i))));
|
||||
}
|
||||
ControllerType ctype =
|
||||
Settings::ParseControllerTypeName(
|
||||
|
|
|
@ -212,6 +212,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="translations\duckstation-qt_es.ts" />
|
||||
<None Include="translations\duckstation-qt_it.ts" />
|
||||
</ItemGroup>
|
||||
<Target Name="CopyCommonDataFiles" AfterTargets="Build" Inputs="@(CommonDataFiles)" Outputs="@(CommonDataFiles -> '$(BinaryOutputDir)%(RecursiveDir)%(Filename)%(Extension)')">
|
||||
<Message Text="Copying common data files" Importance="High" />
|
||||
|
|
|
@ -113,15 +113,28 @@
|
|||
<Image Include="duckstation-qt.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<QtTs Include="translations\duckstation-qt_de.ts" />
|
||||
<QtTs Include="translations\duckstation-qt_he.ts" />
|
||||
<QtTs Include="translations\duckstation-qt_pt-br.ts" />
|
||||
<QtTs Include="translations\duckstation-qt_pt-pt.ts" />
|
||||
<QtTs Include="translations\duckstation-qt_zh-cn.ts" />
|
||||
<QtTs Include="translations\duckstation-qt_de.ts">
|
||||
<Filter>translations</Filter>
|
||||
</QtTs>
|
||||
<QtTs Include="translations\duckstation-qt_he.ts">
|
||||
<Filter>translations</Filter>
|
||||
</QtTs>
|
||||
<QtTs Include="translations\duckstation-qt_pt-br.ts">
|
||||
<Filter>translations</Filter>
|
||||
</QtTs>
|
||||
<QtTs Include="translations\duckstation-qt_pt-pt.ts">
|
||||
<Filter>translations</Filter>
|
||||
</QtTs>
|
||||
<QtTs Include="translations\duckstation-qt_zh-cn.ts">
|
||||
<Filter>translations</Filter>
|
||||
</QtTs>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="translations\duckstation-qt_es.ts">
|
||||
<Filter>translations</Filter>
|
||||
</None>
|
||||
<None Include="translations\duckstation-qt_it.ts">
|
||||
<Filter>translations</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -74,6 +74,11 @@ void GamePropertiesDialog::populate(const GameListEntry* ge)
|
|||
}
|
||||
|
||||
populateTracksInfo(ge->path);
|
||||
|
||||
m_game_code = ge->code;
|
||||
m_game_title = ge->title;
|
||||
m_game_settings = ge->settings;
|
||||
populateGameSettings();
|
||||
}
|
||||
|
||||
void GamePropertiesDialog::populateCompatibilityInfo(const std::string& game_code)
|
||||
|
@ -99,12 +104,50 @@ void GamePropertiesDialog::populateCompatibilityInfo(const std::string& game_cod
|
|||
void GamePropertiesDialog::setupAdditionalUi()
|
||||
{
|
||||
for (u8 i = 0; i < static_cast<u8>(DiscRegion::Count); i++)
|
||||
m_ui.region->addItem(tr(Settings::GetDiscRegionDisplayName(static_cast<DiscRegion>(i))));
|
||||
m_ui.region->addItem(qApp->translate("DiscRegion", Settings::GetDiscRegionDisplayName(static_cast<DiscRegion>(i))));
|
||||
|
||||
for (int i = 0; i < static_cast<int>(GameListCompatibilityRating::Count); i++)
|
||||
{
|
||||
m_ui.compatibility->addItem(
|
||||
tr(GameList::GetGameListCompatibilityRatingString(static_cast<GameListCompatibilityRating>(i))));
|
||||
qApp->translate("GameListCompatibilityRating",
|
||||
GameList::GetGameListCompatibilityRatingString(static_cast<GameListCompatibilityRating>(i))));
|
||||
}
|
||||
|
||||
m_ui.userAspectRatio->addItem(tr("(unchanged)"));
|
||||
for (u32 i = 0; i < static_cast<u32>(DisplayAspectRatio::Count); i++)
|
||||
{
|
||||
m_ui.userAspectRatio->addItem(
|
||||
QString::fromUtf8(Settings::GetDisplayAspectRatioName(static_cast<DisplayAspectRatio>(i))));
|
||||
}
|
||||
|
||||
m_ui.userCropMode->addItem(tr("(unchanged)"));
|
||||
for (u32 i = 0; i < static_cast<u32>(DisplayCropMode::Count); i++)
|
||||
{
|
||||
m_ui.userCropMode->addItem(
|
||||
qApp->translate("DisplayCropMode", Settings::GetDisplayCropModeDisplayName(static_cast<DisplayCropMode>(i))));
|
||||
}
|
||||
|
||||
m_ui.userControllerType1->addItem(tr("(unchanged)"));
|
||||
for (u32 i = 0; i < static_cast<u32>(ControllerType::Count); i++)
|
||||
{
|
||||
m_ui.userControllerType1->addItem(
|
||||
qApp->translate("ControllerType", Settings::GetControllerTypeDisplayName(static_cast<ControllerType>(i))));
|
||||
}
|
||||
|
||||
m_ui.userControllerType2->addItem(tr("(unchanged)"));
|
||||
for (u32 i = 0; i < static_cast<u32>(ControllerType::Count); i++)
|
||||
{
|
||||
m_ui.userControllerType2->addItem(
|
||||
qApp->translate("ControllerType", Settings::GetControllerTypeDisplayName(static_cast<ControllerType>(i))));
|
||||
}
|
||||
|
||||
QGridLayout* traits_layout = new QGridLayout(m_ui.compatibilityTraits);
|
||||
for (u32 i = 0; i < static_cast<u32>(GameSettings::Trait::Count); i++)
|
||||
{
|
||||
m_trait_checkboxes[i] = new QCheckBox(
|
||||
qApp->translate("GameSettingsTrait", GameSettings::GetTraitDisplayName(static_cast<GameSettings::Trait>(i))),
|
||||
m_ui.compatibilityTraits);
|
||||
traits_layout->addWidget(m_trait_checkboxes[i], i / 2, i % 2);
|
||||
}
|
||||
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
@ -133,7 +176,7 @@ void GamePropertiesDialog::populateTracksInfo(const std::string& image_path)
|
|||
{"Audio", "Mode 1", "Mode 1/Raw", "Mode 2", "Mode 2/Form 1", "Mode 2/Form 2", "Mode 2/Mix", "Mode 2/Raw"}};
|
||||
|
||||
m_ui.tracks->clearContents();
|
||||
m_image_path = image_path;
|
||||
m_path = image_path;
|
||||
|
||||
std::unique_ptr<CDImage> image = CDImage::Open(image_path.c_str());
|
||||
if (!image)
|
||||
|
@ -147,14 +190,74 @@ void GamePropertiesDialog::populateTracksInfo(const std::string& image_path)
|
|||
const CDImage::TrackMode mode = image->GetTrackMode(static_cast<u8>(track));
|
||||
const int row = static_cast<int>(track - 1u);
|
||||
m_ui.tracks->insertRow(row);
|
||||
m_ui.tracks->setItem(row, 0, new QTableWidgetItem(tr("%1").arg(track)));
|
||||
m_ui.tracks->setItem(row, 1, new QTableWidgetItem(tr(track_mode_strings[static_cast<u32>(mode)])));
|
||||
m_ui.tracks->setItem(row, 0, new QTableWidgetItem(QStringLiteral("%1").arg(track)));
|
||||
m_ui.tracks->setItem(row, 1, new QTableWidgetItem(track_mode_strings[static_cast<u32>(mode)]));
|
||||
m_ui.tracks->setItem(row, 2, new QTableWidgetItem(MSFTotString(position)));
|
||||
m_ui.tracks->setItem(row, 3, new QTableWidgetItem(MSFTotString(length)));
|
||||
m_ui.tracks->setItem(row, 4, new QTableWidgetItem(tr("<not computed>")));
|
||||
}
|
||||
}
|
||||
|
||||
void GamePropertiesDialog::populateGameSettings()
|
||||
{
|
||||
const GameSettings::Entry& gs = m_game_settings;
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(GameSettings::Trait::Count); i++)
|
||||
{
|
||||
QSignalBlocker sb(m_trait_checkboxes[i]);
|
||||
m_trait_checkboxes[i]->setChecked(gs.HasTrait(static_cast<GameSettings::Trait>(i)));
|
||||
}
|
||||
|
||||
if (gs.display_active_start_offset.has_value())
|
||||
{
|
||||
QSignalBlocker sb(m_ui.displayActiveStartOffset);
|
||||
m_ui.displayActiveStartOffset->setValue(static_cast<int>(gs.display_active_start_offset.value()));
|
||||
}
|
||||
if (gs.display_active_end_offset.has_value())
|
||||
{
|
||||
QSignalBlocker sb(m_ui.displayActiveEndOffset);
|
||||
m_ui.displayActiveEndOffset->setValue(static_cast<int>(gs.display_active_end_offset.value()));
|
||||
}
|
||||
|
||||
if (gs.display_crop_mode.has_value())
|
||||
{
|
||||
QSignalBlocker sb(m_ui.userCropMode);
|
||||
m_ui.userCropMode->setCurrentIndex(static_cast<int>(gs.display_crop_mode.value()) + 1);
|
||||
}
|
||||
if (gs.display_aspect_ratio.has_value())
|
||||
{
|
||||
QSignalBlocker sb(m_ui.userAspectRatio);
|
||||
m_ui.userAspectRatio->setCurrentIndex(static_cast<int>(gs.display_aspect_ratio.value()) + 1);
|
||||
}
|
||||
|
||||
if (gs.controller_1_type.has_value())
|
||||
{
|
||||
QSignalBlocker sb(m_ui.userControllerType1);
|
||||
m_ui.userControllerType1->setCurrentIndex(static_cast<int>(gs.controller_1_type.value()) + 1);
|
||||
}
|
||||
if (gs.controller_2_type.has_value())
|
||||
{
|
||||
QSignalBlocker sb(m_ui.userControllerType2);
|
||||
m_ui.userControllerType2->setCurrentIndex(static_cast<int>(gs.controller_2_type.value()) + 1);
|
||||
}
|
||||
if (gs.gpu_widescreen_hack.has_value())
|
||||
{
|
||||
QSignalBlocker sb(m_ui.userControllerType2);
|
||||
m_ui.userWidescreenHack->setCheckState(Qt::Checked);
|
||||
}
|
||||
else
|
||||
{
|
||||
QSignalBlocker sb(m_ui.userControllerType2);
|
||||
m_ui.userWidescreenHack->setCheckState(Qt::PartiallyChecked);
|
||||
}
|
||||
}
|
||||
|
||||
void GamePropertiesDialog::saveGameSettings()
|
||||
{
|
||||
m_host_interface->getGameList()->UpdateGameSettings(m_path, m_game_code, m_game_title, m_game_settings, true, true);
|
||||
m_host_interface->applySettings(true);
|
||||
}
|
||||
|
||||
void GamePropertiesDialog::closeEvent(QCloseEvent* ev)
|
||||
{
|
||||
deleteLater();
|
||||
|
@ -186,6 +289,69 @@ void GamePropertiesDialog::connectUi()
|
|||
connect(m_ui.exportCompatibilityInfo, &QPushButton::clicked, this,
|
||||
&GamePropertiesDialog::onExportCompatibilityInfoClicked);
|
||||
connect(m_ui.close, &QPushButton::clicked, this, &QDialog::close);
|
||||
|
||||
connect(m_ui.userAspectRatio, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
|
||||
if (index <= 0)
|
||||
m_game_settings.display_aspect_ratio.reset();
|
||||
else
|
||||
m_game_settings.display_aspect_ratio = static_cast<DisplayAspectRatio>(index - 1);
|
||||
saveGameSettings();
|
||||
});
|
||||
|
||||
connect(m_ui.userCropMode, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
|
||||
if (index <= 0)
|
||||
m_game_settings.display_crop_mode.reset();
|
||||
else
|
||||
m_game_settings.display_crop_mode = static_cast<DisplayCropMode>(index - 1);
|
||||
saveGameSettings();
|
||||
});
|
||||
|
||||
connect(m_ui.userControllerType1, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
|
||||
if (index <= 0)
|
||||
m_game_settings.controller_1_type.reset();
|
||||
else
|
||||
m_game_settings.controller_1_type = static_cast<ControllerType>(index - 1);
|
||||
saveGameSettings();
|
||||
});
|
||||
|
||||
connect(m_ui.userControllerType2, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) {
|
||||
if (index <= 0)
|
||||
m_game_settings.controller_2_type.reset();
|
||||
else
|
||||
m_game_settings.controller_2_type = static_cast<ControllerType>(index - 1);
|
||||
saveGameSettings();
|
||||
});
|
||||
|
||||
connect(m_ui.userWidescreenHack, &QCheckBox::stateChanged, [this](int state) {
|
||||
if (state == Qt::PartiallyChecked)
|
||||
m_game_settings.gpu_widescreen_hack.reset();
|
||||
else
|
||||
m_game_settings.gpu_widescreen_hack = (state == Qt::Checked);
|
||||
saveGameSettings();
|
||||
});
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(GameSettings::Trait::Count); i++)
|
||||
{
|
||||
connect(m_trait_checkboxes[i], &QCheckBox::toggled, [this, i](bool checked) {
|
||||
m_game_settings.SetTrait(static_cast<GameSettings::Trait>(i), checked);
|
||||
saveGameSettings();
|
||||
});
|
||||
}
|
||||
|
||||
connect(m_ui.displayActiveStartOffset, QOverload<int>::of(&QSpinBox::valueChanged), [this](int value) {
|
||||
if (value == 0)
|
||||
m_game_settings.display_active_start_offset.reset();
|
||||
else
|
||||
m_game_settings.display_active_start_offset = static_cast<s16>(value);
|
||||
saveGameSettings();
|
||||
});
|
||||
connect(m_ui.displayActiveEndOffset, QOverload<int>::of(&QSpinBox::valueChanged), [this](int value) {
|
||||
if (value == 0)
|
||||
m_game_settings.display_active_end_offset.reset();
|
||||
else
|
||||
m_game_settings.display_active_end_offset = static_cast<s16>(value);
|
||||
saveGameSettings();
|
||||
});
|
||||
}
|
||||
|
||||
void GamePropertiesDialog::fillEntryFromUi(GameListCompatibilityEntry* entry)
|
||||
|
@ -263,10 +429,10 @@ void GamePropertiesDialog::onExportCompatibilityInfoClicked()
|
|||
|
||||
void GamePropertiesDialog::computeTrackHashes()
|
||||
{
|
||||
if (m_image_path.empty())
|
||||
if (m_path.empty())
|
||||
return;
|
||||
|
||||
std::unique_ptr<CDImage> image = CDImage::Open(m_image_path.c_str());
|
||||
std::unique_ptr<CDImage> image = CDImage::Open(m_path.c_str());
|
||||
if (!image)
|
||||
return;
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
#include "core/game_settings.h"
|
||||
#include "ui_gamepropertiesdialog.h"
|
||||
#include <QtWidgets/QDialog>
|
||||
#include <array>
|
||||
|
||||
struct GameListEntry;
|
||||
struct GameListCompatibilityEntry;
|
||||
|
@ -40,15 +42,22 @@ private:
|
|||
void connectUi();
|
||||
void populateCompatibilityInfo(const std::string& game_code);
|
||||
void populateTracksInfo(const std::string& image_path);
|
||||
void populateGameSettings();
|
||||
void saveGameSettings();
|
||||
void fillEntryFromUi(GameListCompatibilityEntry* entry);
|
||||
void computeTrackHashes();
|
||||
void onResize();
|
||||
|
||||
Ui::GamePropertiesDialog m_ui;
|
||||
std::array<QCheckBox*, static_cast<u32>(GameSettings::Trait::Count)> m_trait_checkboxes{};
|
||||
|
||||
QtHostInterface* m_host_interface;
|
||||
|
||||
std::string m_image_path;
|
||||
std::string m_path;
|
||||
std::string m_game_code;
|
||||
std::string m_game_title;
|
||||
|
||||
GameSettings::Entry m_game_settings;
|
||||
|
||||
bool m_compatibility_info_changed = false;
|
||||
bool m_tracks_hashed = false;
|
||||
|
|
|
@ -6,171 +6,346 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>722</width>
|
||||
<height>466</height>
|
||||
<width>793</width>
|
||||
<height>647</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="resources/icons.qrc">
|
||||
<iconset>
|
||||
<normaloff>:/icons/duck.png</normaloff>:/icons/duck.png</iconset>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Image Path:</string>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Properties</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Image Path:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="imagePath">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Game Code:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="gameCode">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Title:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="title">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Region:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="region">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Compatibility:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="compatibility"/>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Upscaling Issues:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLineEdit" name="upscalingIssues"/>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Comments:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="comments"/>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Version Tested:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="versionTested"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="setToCurrent">
|
||||
<property name="text">
|
||||
<string>Set to Current</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Tracks:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<widget class="QTableWidget" name="tracks">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>#</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Mode</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Length</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Hash</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_3">
|
||||
<attribute name="title">
|
||||
<string>User Settings</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4">
|
||||
<property name="title">
|
||||
<string>GPU Settings</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_5">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="text">
|
||||
<string>Crop Mode:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="userCropMode"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>Aspect Ratio:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="userAspectRatio"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="userWidescreenHack">
|
||||
<property name="text">
|
||||
<string>Widescreen Hack</string>
|
||||
</property>
|
||||
<property name="tristate">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>Controller Settings</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_4">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="text">
|
||||
<string>Controller 1 Type:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="userControllerType1"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_16">
|
||||
<property name="text">
|
||||
<string>Controller 2 Type:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="userControllerType2"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Compatibility Settings</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="compatibilityTraits">
|
||||
<property name="title">
|
||||
<string>Traits</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="compatibilityOverrides">
|
||||
<property name="title">
|
||||
<string>Overrides</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Display Active Offset:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QSpinBox" name="displayActiveStartOffset">
|
||||
<property name="minimum">
|
||||
<number>-5000</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>5000</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="displayActiveEndOffset">
|
||||
<property name="minimum">
|
||||
<number>-5000</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>5000</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="imagePath">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Game Code:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="gameCode">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Title:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="title">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Region:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QComboBox" name="region">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Compatibility:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="compatibility"/>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Upscaling Issues:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLineEdit" name="upscalingIssues"/>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Comments:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="comments"/>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Version Tested:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="versionTested"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="setToCurrent">
|
||||
<property name="text">
|
||||
<string>Set to Current</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Tracks:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QTableWidget" name="tracks">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>#</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Mode</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Length</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Hash</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0" colspan="2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
|
|
|
@ -16,12 +16,15 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW
|
|||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.confirmPowerOff, "Main", "ConfirmPowerOff", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.loadDevicesFromSaveStates, "Main",
|
||||
"LoadDevicesFromSaveStates", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.applyGameSettings, "Main", "ApplyGameSettings",
|
||||
true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.showOSDMessages, "Display", "ShowOSDMessages",
|
||||
true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.showFPS, "Display", "ShowFPS", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.showVPS, "Display", "ShowVPS", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.showSpeed, "Display", "ShowSpeed", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.showResolution, "Display", "ShowResolution", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.showResolution, "Display", "ShowResolution",
|
||||
false);
|
||||
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.enableSpeedLimiter, "Main", "SpeedLimiterEnabled",
|
||||
true);
|
||||
|
@ -57,6 +60,10 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW
|
|||
tr("When enabled, memory cards and controllers will be overwritten when save states are loaded. This can "
|
||||
"result in lost saves, and controller type mismatches. For deterministic save states, enable this option, "
|
||||
"otherwise leave disabled."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.applyGameSettings, tr("Apply Per-Game Settings"), tr("Checked"),
|
||||
tr("When enabled, per-game settings will be applied, and incompatible enhancements will be disabled. You should "
|
||||
"leave this option enabled except when testing enhancements with incompatible games."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.enableSpeedLimiter, tr("Enable Speed Limiter"), tr("Checked"),
|
||||
tr("Throttles the emulation speed to the chosen speed above. If unchecked, the emulator will "
|
||||
|
@ -82,26 +89,33 @@ GeneralSettingsWidget::GeneralSettingsWidget(QtHostInterface* host_interface, QW
|
|||
tr("Shows the current emulation speed of the system in the top-right corner of the display as a percentage."));
|
||||
|
||||
// Since this one is compile-time selected, we don't put it in the .ui file.
|
||||
const int last_row_count = m_ui.formLayout_4->rowCount();
|
||||
int current_col = 1;
|
||||
int current_row = m_ui.formLayout_4->rowCount() - current_col;
|
||||
#ifdef WITH_DISCORD_PRESENCE
|
||||
{
|
||||
QCheckBox* enableDiscordPresence = new QCheckBox(tr("Enable Discord Presence"), m_ui.groupBox_4);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, enableDiscordPresence, "Main",
|
||||
"EnableDiscordPresence");
|
||||
m_ui.formLayout_4->addWidget(enableDiscordPresence, last_row_count, 0);
|
||||
m_ui.formLayout_4->addWidget(enableDiscordPresence, current_row, current_col);
|
||||
dialog->registerWidgetHelp(enableDiscordPresence, tr("Enable Discord Presence"), tr("Unchecked"),
|
||||
tr("Shows the game you are currently playing as part of your profile in Discord."));
|
||||
current_col++;
|
||||
current_row += (current_col / 2);
|
||||
current_col %= 2;
|
||||
}
|
||||
#endif
|
||||
if (AutoUpdaterDialog::isSupported())
|
||||
{
|
||||
QCheckBox* enableDiscordPresence = new QCheckBox(tr("Enable Automatic Update Check"), m_ui.groupBox_4);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, enableDiscordPresence, "AutoUpdater",
|
||||
"CheckAtStartup");
|
||||
m_ui.formLayout_4->addWidget(enableDiscordPresence, last_row_count, 1);
|
||||
"CheckAtStartup", true);
|
||||
m_ui.formLayout_4->addWidget(enableDiscordPresence, current_row, current_col);
|
||||
dialog->registerWidgetHelp(enableDiscordPresence, tr("Enable Automatic Update Check"), tr("Checked"),
|
||||
tr("Automatically checks for updates to the program on startup. Updates can be deferred "
|
||||
"until later or skipped entirely."));
|
||||
current_col++;
|
||||
current_row += (current_col / 2);
|
||||
current_col %= 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,20 @@
|
|||
<string>Behaviour</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="formLayout_4">
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="confirmPowerOff">
|
||||
<property name="text">
|
||||
<string>Confirm Power Off</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="renderToMain">
|
||||
<property name="text">
|
||||
<string>Render To Main Window</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="pauseOnStart">
|
||||
<property name="text">
|
||||
|
@ -39,10 +53,10 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="confirmPowerOff">
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="startFullscreen">
|
||||
<property name="text">
|
||||
<string>Confirm Power Off</string>
|
||||
<string>Start Fullscreen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -60,17 +74,10 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="startFullscreen">
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="applyGameSettings">
|
||||
<property name="text">
|
||||
<string>Start Fullscreen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="renderToMain">
|
||||
<property name="text">
|
||||
<string>Render To Main Window</string>
|
||||
<string>Apply Per-Game Settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -44,6 +44,7 @@ GPUSettingsWidget::GPUSettingsWidget(QtHostInterface* host_interface, QWidget* p
|
|||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.pgxpTextureCorrection, "GPU",
|
||||
"PGXPTextureCorrection", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.pgxpVertexCache, "GPU", "PGXPVertexCache", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.pgxpCPUMode, "GPU", "PGXPCPUMode", false);
|
||||
|
||||
connect(m_ui.resolutionScale, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
|
||||
&GPUSettingsWidget::updateScaledDitheringEnabled);
|
||||
|
@ -128,7 +129,8 @@ GPUSettingsWidget::GPUSettingsWidget(QtHostInterface* host_interface, QWidget* p
|
|||
m_ui.widescreenHack, tr("Widescreen Hack"), tr("Unchecked"),
|
||||
tr("Scales vertex positions in screen-space to a widescreen aspect ratio, essentially "
|
||||
"increasing the field of view from 4:3 to 16:9 in 3D games. <br>For 2D games, or games which "
|
||||
"use pre-rendered backgrounds, this enhancement will not work as expected. <b><u>May not be compatible with all games.</u></b>"));
|
||||
"use pre-rendered backgrounds, this enhancement will not work as expected. <b><u>May not be compatible with all "
|
||||
"games.</u></b>"));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.pgxpEnable, tr("Geometry Correction"), tr("Unchecked"),
|
||||
tr("Reduces \"wobbly\" polygons and \"warping\" textures that are common in PS1 games. <br>Only "
|
||||
|
@ -142,6 +144,10 @@ GPUSettingsWidget::GPUSettingsWidget(QtHostInterface* host_interface, QWidget* p
|
|||
dialog->registerWidgetHelp(m_ui.pgxpVertexCache, tr("Vertex Cache"), tr("Unchecked"),
|
||||
tr("Uses screen coordinates as a fallback when tracking vertices through memory fails. "
|
||||
"May improve PGXP compatibility."));
|
||||
dialog->registerWidgetHelp(
|
||||
m_ui.pgxpCPUMode, tr("CPU Mode"), tr("Unchecked"),
|
||||
tr("Tries to track vertex manipulation through the CPU. Some games require this option for PGXP to be effective. "
|
||||
"Very slow, and incompatible with the recompiler."));
|
||||
}
|
||||
|
||||
GPUSettingsWidget::~GPUSettingsWidget() = default;
|
||||
|
@ -157,7 +163,10 @@ void GPUSettingsWidget::updateScaledDitheringEnabled()
|
|||
void GPUSettingsWidget::setupAdditionalUi()
|
||||
{
|
||||
for (u32 i = 0; i < static_cast<u32>(GPURenderer::Count); i++)
|
||||
m_ui.renderer->addItem(QString::fromUtf8(Settings::GetRendererDisplayName(static_cast<GPURenderer>(i))));
|
||||
{
|
||||
m_ui.renderer->addItem(
|
||||
qApp->translate("GPURenderer", Settings::GetRendererDisplayName(static_cast<GPURenderer>(i))));
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(DisplayAspectRatio::Count); i++)
|
||||
{
|
||||
|
@ -168,7 +177,7 @@ void GPUSettingsWidget::setupAdditionalUi()
|
|||
for (u32 i = 0; i < static_cast<u32>(DisplayCropMode::Count); i++)
|
||||
{
|
||||
m_ui.displayCropMode->addItem(
|
||||
QString::fromUtf8(Settings::GetDisplayCropModeDisplayName(static_cast<DisplayCropMode>(i))));
|
||||
qApp->translate("DisplayCropMode", Settings::GetDisplayCropModeDisplayName(static_cast<DisplayCropMode>(i))));
|
||||
}
|
||||
|
||||
std::array<QString, GPU::MAX_RESOLUTION_SCALE + 1> resolution_suffixes = {{
|
||||
|
@ -255,4 +264,5 @@ void GPUSettingsWidget::updatePGXPSettingsEnabled()
|
|||
m_ui.pgxpCulling->setEnabled(enabled);
|
||||
m_ui.pgxpTextureCorrection->setEnabled(enabled);
|
||||
m_ui.pgxpVertexCache->setEnabled(enabled);
|
||||
m_ui.pgxpCPUMode->setEnabled(enabled);
|
||||
}
|
||||
|
|
|
@ -215,6 +215,13 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="pgxpCPUMode">
|
||||
<property name="text">
|
||||
<string>CPU Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "inputbindingwidgets.h"
|
||||
#include "qthostinterface.h"
|
||||
#include "qtutils.h"
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtGui/QKeyEvent>
|
||||
#include <QtWidgets/QGridLayout>
|
||||
|
@ -63,7 +64,7 @@ void HotkeySettingsWidget::createButtons()
|
|||
|
||||
std::string section_name("Hotkeys");
|
||||
std::string key_name(hi.name.GetCharArray());
|
||||
layout->addWidget(new QLabel(QString::fromUtf8(hi.display_name), container), target_row, 0);
|
||||
layout->addWidget(new QLabel(qApp->translate("Hotkeys", hi.display_name), container), target_row, 0);
|
||||
layout->addWidget(
|
||||
new InputButtonBindingWidget(m_host_interface, std::move(section_name), std::move(key_name), container),
|
||||
target_row, 1);
|
||||
|
|
|
@ -310,6 +310,34 @@ void MainWindow::onRemoveDiscActionTriggered()
|
|||
m_host_interface->changeDisc(QString());
|
||||
}
|
||||
|
||||
void MainWindow::onViewToolbarActionToggled(bool checked)
|
||||
{
|
||||
m_host_interface->SetBoolSettingValue("UI", "ShowToolbar", checked);
|
||||
m_ui.toolBar->setVisible(checked);
|
||||
}
|
||||
|
||||
void MainWindow::onViewStatusBarActionToggled(bool checked)
|
||||
{
|
||||
m_host_interface->SetBoolSettingValue("UI", "ShowStatusBar", checked);
|
||||
m_ui.statusBar->setVisible(checked);
|
||||
}
|
||||
|
||||
void MainWindow::onViewGameListActionTriggered()
|
||||
{
|
||||
if (m_emulation_running)
|
||||
m_host_interface->pauseSystem(true);
|
||||
switchToGameListView();
|
||||
}
|
||||
|
||||
void MainWindow::onViewSystemDisplayTriggered()
|
||||
{
|
||||
if (m_emulation_running)
|
||||
{
|
||||
switchToEmulationView();
|
||||
m_host_interface->pauseSystem(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onGitHubRepositoryActionTriggered()
|
||||
{
|
||||
QtUtils::OpenURL(this, "https://github.com/stenzek/duckstation/");
|
||||
|
@ -431,6 +459,14 @@ void MainWindow::setupAdditionalUi()
|
|||
{
|
||||
setWindowTitle(getWindowTitle());
|
||||
|
||||
const bool toolbar_visible = m_host_interface->GetBoolSettingValue("UI", "ShowToolbar", true);
|
||||
m_ui.actionViewToolbar->setChecked(toolbar_visible);
|
||||
m_ui.toolBar->setVisible(toolbar_visible);
|
||||
|
||||
const bool status_bar_visible = m_host_interface->GetBoolSettingValue("UI", "ShowStatusBar", true);
|
||||
m_ui.actionViewStatusBar->setChecked(status_bar_visible);
|
||||
m_ui.statusBar->setVisible(status_bar_visible);
|
||||
|
||||
m_game_list_widget = new GameListWidget(m_ui.mainContainer);
|
||||
m_game_list_widget->initialize(m_host_interface);
|
||||
m_ui.mainContainer->insertWidget(0, m_game_list_widget);
|
||||
|
@ -451,6 +487,8 @@ void MainWindow::setupAdditionalUi()
|
|||
m_status_frame_time_widget->setFixedSize(190, 16);
|
||||
m_status_frame_time_widget->hide();
|
||||
|
||||
updateDebugMenuVisibility();
|
||||
|
||||
for (u32 i = 0; i < static_cast<u32>(CPUExecutionMode::Count); i++)
|
||||
{
|
||||
const CPUExecutionMode mode = static_cast<CPUExecutionMode>(i);
|
||||
|
@ -507,6 +545,7 @@ void MainWindow::updateEmulationActions(bool starting, bool running)
|
|||
m_ui.actionPause->setDisabled(starting || !running);
|
||||
m_ui.actionChangeDisc->setDisabled(starting || !running);
|
||||
m_ui.actionScreenshot->setDisabled(starting || !running);
|
||||
m_ui.actionViewSystemDisplay->setEnabled(starting || running);
|
||||
m_ui.menuChangeDisc->setDisabled(starting || !running);
|
||||
|
||||
m_ui.actionSaveState->setDisabled(starting || !running);
|
||||
|
@ -615,6 +654,10 @@ void MainWindow::connectSignals()
|
|||
[this]() { doSettings(SettingsDialog::Category::AudioSettings); });
|
||||
connect(m_ui.actionAdvancedSettings, &QAction::triggered,
|
||||
[this]() { doSettings(SettingsDialog::Category::AdvancedSettings); });
|
||||
connect(m_ui.actionViewToolbar, &QAction::toggled, this, &MainWindow::onViewToolbarActionToggled);
|
||||
connect(m_ui.actionViewStatusBar, &QAction::toggled, this, &MainWindow::onViewStatusBarActionToggled);
|
||||
connect(m_ui.actionViewGameList, &QAction::triggered, this, &MainWindow::onViewGameListActionTriggered);
|
||||
connect(m_ui.actionViewSystemDisplay, &QAction::triggered, this, &MainWindow::onViewSystemDisplayTriggered);
|
||||
connect(m_ui.actionGitHubRepository, &QAction::triggered, this, &MainWindow::onGitHubRepositoryActionTriggered);
|
||||
connect(m_ui.actionIssueTracker, &QAction::triggered, this, &MainWindow::onIssueTrackerActionTriggered);
|
||||
connect(m_ui.actionDiscordServer, &QAction::triggered, this, &MainWindow::onDiscordServerActionTriggered);
|
||||
|
@ -847,6 +890,12 @@ void MainWindow::startupUpdateCheck()
|
|||
checkForUpdates(false);
|
||||
}
|
||||
|
||||
void MainWindow::updateDebugMenuVisibility()
|
||||
{
|
||||
const bool visible = m_host_interface->GetBoolSettingValue("Main", "ShowDebugMenu", false);
|
||||
m_ui.menuDebug->menuAction()->setVisible(visible);
|
||||
}
|
||||
|
||||
void MainWindow::checkForUpdates(bool display_message)
|
||||
{
|
||||
if (!AutoUpdaterDialog::isSupported())
|
||||
|
|
|
@ -29,6 +29,10 @@ public:
|
|||
/// Performs update check if enabled in settings.
|
||||
void startupUpdateCheck();
|
||||
|
||||
public Q_SLOTS:
|
||||
/// Updates debug menu visibility (hides if disabled).
|
||||
void updateDebugMenuVisibility();
|
||||
|
||||
private Q_SLOTS:
|
||||
void reportError(const QString& message);
|
||||
void reportMessage(const QString& message);
|
||||
|
@ -58,6 +62,10 @@ private Q_SLOTS:
|
|||
void onChangeDiscFromPlaylistMenuAboutToShow();
|
||||
void onChangeDiscFromPlaylistMenuAboutToHide();
|
||||
void onRemoveDiscActionTriggered();
|
||||
void onViewToolbarActionToggled(bool checked);
|
||||
void onViewStatusBarActionToggled(bool checked);
|
||||
void onViewGameListActionTriggered();
|
||||
void onViewSystemDisplayTriggered();
|
||||
void onGitHubRepositoryActionTriggered();
|
||||
void onIssueTrackerActionTriggered();
|
||||
void onDiscordServerActionTriggered();
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>754</width>
|
||||
<height>22</height>
|
||||
<height>30</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuSystem">
|
||||
|
@ -161,8 +161,19 @@
|
|||
<addaction name="actionDebugShowTimersState"/>
|
||||
<addaction name="actionDebugShowMDECState"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menu_View">
|
||||
<property name="title">
|
||||
<string>&View</string>
|
||||
</property>
|
||||
<addaction name="actionViewToolbar"/>
|
||||
<addaction name="actionViewStatusBar"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionViewGameList"/>
|
||||
<addaction name="actionViewSystemDisplay"/>
|
||||
</widget>
|
||||
<addaction name="menuSystem"/>
|
||||
<addaction name="menuSettings"/>
|
||||
<addaction name="menu_View"/>
|
||||
<addaction name="menuDebug"/>
|
||||
<addaction name="menuHelp"/>
|
||||
</widget>
|
||||
|
@ -560,6 +571,41 @@
|
|||
<string>Resumes the last save state created.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewToolbar">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Toolbar</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewStatusBar">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Status Bar</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewGameList">
|
||||
<property name="text">
|
||||
<string>&Game List</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionViewSystemDisplay">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>System &Display</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="resources/icons.qrc"/>
|
||||
|
|
|
@ -76,7 +76,7 @@ void MemoryCardSettingsWidget::createPortSettingsUi(SettingsDialog* dialog, int
|
|||
for (int i = 0; i < static_cast<int>(MemoryCardType::Count); i++)
|
||||
{
|
||||
ui->memory_card_type->addItem(
|
||||
QString::fromUtf8(Settings::GetMemoryCardTypeDisplayName(static_cast<MemoryCardType>(i))));
|
||||
qApp->translate("MemoryCardType", Settings::GetMemoryCardTypeDisplayName(static_cast<MemoryCardType>(i))));
|
||||
}
|
||||
|
||||
const MemoryCardType default_value = (index == 0) ? MemoryCardType::PerGameTitle : MemoryCardType::None;
|
||||
|
|
|
@ -34,7 +34,10 @@
|
|||
Log_SetChannel(QtHostInterface);
|
||||
|
||||
#ifdef WIN32
|
||||
#include "common/windows_headers.h"
|
||||
#include "frontend-common/d3d11_host_display.h"
|
||||
#include <KnownFolders.h>
|
||||
#include <ShlObj.h>
|
||||
#endif
|
||||
|
||||
QtHostInterface::QtHostInterface(QObject* parent) : QObject(parent), CommonHostInterface()
|
||||
|
@ -58,6 +61,7 @@ std::vector<std::pair<QString, QString>> QtHostInterface::getAvailableLanguageLi
|
|||
{QStringLiteral("Deutsch"), QStringLiteral("de")},
|
||||
{QStringLiteral("Español"), QStringLiteral("es")},
|
||||
{QStringLiteral("עברית"), QStringLiteral("he")},
|
||||
{QStringLiteral("Italiano"), QStringLiteral("it")},
|
||||
{QStringLiteral("Português (Pt)"), QStringLiteral("pt-pt")},
|
||||
{QStringLiteral("Português (Br)"), QStringLiteral("pt-br")},
|
||||
{QStringLiteral("简体中文"), QStringLiteral("zh-cn")}};
|
||||
|
@ -279,16 +283,18 @@ void QtHostInterface::setDefaultSettings()
|
|||
m_settings_interface->Save();
|
||||
|
||||
CommonHostInterface::LoadSettings(*m_settings_interface.get());
|
||||
CommonHostInterface::ApplyGameSettings(false);
|
||||
CommonHostInterface::FixIncompatibleSettings(false);
|
||||
}
|
||||
|
||||
CheckForSettingsChanges(old_settings);
|
||||
}
|
||||
|
||||
void QtHostInterface::applySettings()
|
||||
void QtHostInterface::applySettings(bool display_osd_messages /* = false */)
|
||||
{
|
||||
if (!isOnWorkerThread())
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "applySettings", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, "applySettings", Qt::QueuedConnection, Q_ARG(bool, display_osd_messages));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -296,6 +302,8 @@ void QtHostInterface::applySettings()
|
|||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(m_settings_mutex);
|
||||
CommonHostInterface::LoadSettings(*m_settings_interface.get());
|
||||
CommonHostInterface::ApplyGameSettings(display_osd_messages);
|
||||
CommonHostInterface::FixIncompatibleSettings(display_osd_messages);
|
||||
}
|
||||
|
||||
CheckForSettingsChanges(old_settings);
|
||||
|
@ -648,6 +656,7 @@ void QtHostInterface::OnSystemPerformanceCountersUpdated()
|
|||
void QtHostInterface::OnRunningGameChanged()
|
||||
{
|
||||
CommonHostInterface::OnRunningGameChanged();
|
||||
applySettings(true);
|
||||
|
||||
if (!System::IsShutdown())
|
||||
{
|
||||
|
@ -672,6 +681,7 @@ void QtHostInterface::LoadSettings()
|
|||
|
||||
CommonHostInterface::CheckSettings(*m_settings_interface.get());
|
||||
CommonHostInterface::LoadSettings(*m_settings_interface.get());
|
||||
CommonHostInterface::FixIncompatibleSettings(false);
|
||||
}
|
||||
|
||||
void QtHostInterface::SetDefaultSettings(SettingsInterface& si)
|
||||
|
@ -1179,6 +1189,49 @@ void QtHostInterface::wakeThread()
|
|||
QMetaObject::invokeMethod(m_worker_thread_event_loop, "quit", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
static std::string GetFontPath(const char* name)
|
||||
{
|
||||
#ifdef WIN32
|
||||
PWSTR folder_path;
|
||||
if (FAILED(SHGetKnownFolderPath(FOLDERID_Fonts, 0, nullptr, &folder_path)))
|
||||
return StringUtil::StdStringFromFormat("C:\\Windows\\Fonts\\%s", name);
|
||||
|
||||
std::string font_path(StringUtil::WideStringToUTF8String(folder_path));
|
||||
CoTaskMemFree(folder_path);
|
||||
font_path += "\\";
|
||||
font_path += name;
|
||||
return font_path;
|
||||
#else
|
||||
return name;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool AddImGuiFont(const std::string& language, float size, float framebuffer_scale)
|
||||
{
|
||||
std::string path;
|
||||
const ImWchar* range = nullptr;
|
||||
#ifdef WIN32
|
||||
if (language == "jp")
|
||||
{
|
||||
path = GetFontPath("msgothic.ttc");
|
||||
range = ImGui::GetIO().Fonts->GetGlyphRangesJapanese();
|
||||
}
|
||||
else if (language == "zh-cn")
|
||||
{
|
||||
path = GetFontPath("msyh.ttc");
|
||||
range = ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!path.empty())
|
||||
{
|
||||
return (ImGui::GetIO().Fonts->AddFontFromFileTTF(path.c_str(), size * framebuffer_scale, nullptr, range) !=
|
||||
nullptr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QtHostInterface::createImGuiContext(float framebuffer_scale)
|
||||
{
|
||||
ImGui::CreateContext();
|
||||
|
@ -1190,7 +1243,10 @@ void QtHostInterface::createImGuiContext(float framebuffer_scale)
|
|||
ImGui::GetStyle().ScaleAllSizes(framebuffer_scale);
|
||||
|
||||
ImGui::StyleColorsDarker();
|
||||
ImGui::AddRobotoRegularFont(15.0f * framebuffer_scale);
|
||||
|
||||
std::string language = GetStringSettingValue("Main", "Language", "");
|
||||
if (!AddImGuiFont(language, 15.0f, framebuffer_scale))
|
||||
ImGui::AddRobotoRegularFont(15.0f * framebuffer_scale);
|
||||
}
|
||||
|
||||
void QtHostInterface::destroyImGuiContext()
|
||||
|
@ -1198,6 +1254,24 @@ void QtHostInterface::destroyImGuiContext()
|
|||
ImGui::DestroyContext();
|
||||
}
|
||||
|
||||
TinyString QtHostInterface::TranslateString(const char* context, const char* str) const
|
||||
{
|
||||
const QString translated(m_translator->translate(context, str));
|
||||
if (translated.isEmpty())
|
||||
return TinyString(str);
|
||||
|
||||
return TinyString(translated.toUtf8().constData());
|
||||
}
|
||||
|
||||
std::string QtHostInterface::TranslateStdString(const char* context, const char* str) const
|
||||
{
|
||||
const QString translated(m_translator->translate(context, str));
|
||||
if (translated.isEmpty())
|
||||
return std::string(str);
|
||||
|
||||
return translated.toStdString();
|
||||
}
|
||||
|
||||
QtHostInterface::Thread::Thread(QtHostInterface* parent) : QThread(parent), m_parent(parent) {}
|
||||
|
||||
QtHostInterface::Thread::~Thread() = default;
|
||||
|
|
|
@ -64,6 +64,9 @@ public:
|
|||
void SetStringListSettingValue(const char* section, const char* key, const std::vector<std::string>& values);
|
||||
void RemoveSettingValue(const char* section, const char* key);
|
||||
|
||||
TinyString TranslateString(const char* context, const char* str) const;
|
||||
std::string TranslateStdString(const char* context, const char* str) const;
|
||||
|
||||
ALWAYS_INLINE const GameList* getGameList() const { return m_game_list.get(); }
|
||||
ALWAYS_INLINE GameList* getGameList() { return m_game_list.get(); }
|
||||
void refreshGameList(bool invalidate_cache = false, bool invalidate_database = false);
|
||||
|
@ -129,7 +132,7 @@ Q_SIGNALS:
|
|||
|
||||
public Q_SLOTS:
|
||||
void setDefaultSettings();
|
||||
void applySettings();
|
||||
void applySettings(bool display_osd_messages = false);
|
||||
void updateInputMap();
|
||||
void applyInputProfile(const QString& profile_path);
|
||||
void onDisplayWindowKeyEvent(int key, bool pressed);
|
||||
|
|
2110
src/duckstation-qt/translations/duckstation-qt_it.ts
Normal file
2110
src/duckstation-qt/translations/duckstation-qt_it.ts
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,10 @@
|
|||
..\..\dep\msvc\qt\5.15.0\msvc2017_64\bin\lupdate.exe ./ -ts translations\duckstation-qt_de.ts
|
||||
..\..\dep\msvc\qt\5.15.0\msvc2017_64\bin\lupdate.exe ./ -ts translations\duckstation-qt_he.ts
|
||||
..\..\dep\msvc\qt\5.15.0\msvc2017_64\bin\lupdate.exe ./ -ts translations\duckstation-qt_pt-br.ts
|
||||
..\..\dep\msvc\qt\5.15.0\msvc2017_64\bin\lupdate.exe ./ -ts translations\duckstation-qt_pt-pt.ts
|
||||
..\..\dep\msvc\qt\5.15.0\msvc2017_64\bin\lupdate.exe ./ -ts translations\duckstation-qt_zh-cn.ts
|
||||
set LUPDATE=..\..\dep\msvc\qt\5.15.0\msvc2017_64\bin\lupdate.exe ./ ../core/ ../frontend-common/ -tr-function-alias translate+=TranslateString -tr-function-alias translate+=TranslateStdString -tr-function-alias QT_TRANSLATE_NOOP+=TRANSLATABLE
|
||||
|
||||
%LUPDATE% -ts translations\duckstation-qt_de.ts
|
||||
%LUPDATE% -ts translations\duckstation-qt_es.ts
|
||||
%LUPDATE% -ts translations\duckstation-qt_he.ts
|
||||
%LUPDATE% -ts translations\duckstation-qt_it.ts
|
||||
%LUPDATE% -ts translations\duckstation-qt_pt-br.ts
|
||||
%LUPDATE% -ts translations\duckstation-qt_pt-pt.ts
|
||||
%LUPDATE% -ts translations\duckstation-qt_zh-cn.ts
|
||||
pause
|
||||
|
|
|
@ -321,6 +321,12 @@ void SDLHostInterface::OnRunningGameChanged()
|
|||
{
|
||||
CommonHostInterface::OnRunningGameChanged();
|
||||
|
||||
Settings old_settings(std::move(g_settings));
|
||||
CommonHostInterface::LoadSettings(*m_settings_interface.get());
|
||||
CommonHostInterface::ApplyGameSettings(true);
|
||||
CommonHostInterface::FixIncompatibleSettings(true);
|
||||
CheckForSettingsChanges(old_settings);
|
||||
|
||||
if (!System::GetRunningTitle().empty())
|
||||
SDL_SetWindowTitle(m_window, System::GetRunningTitle().c_str());
|
||||
else
|
||||
|
@ -347,6 +353,8 @@ void SDLHostInterface::SaveAndUpdateSettings()
|
|||
|
||||
Settings old_settings(std::move(g_settings));
|
||||
CommonHostInterface::LoadSettings(*m_settings_interface.get());
|
||||
CommonHostInterface::ApplyGameSettings(false);
|
||||
CommonHostInterface::FixIncompatibleSettings(false);
|
||||
CheckForSettingsChanges(old_settings);
|
||||
|
||||
m_settings_interface->Save();
|
||||
|
@ -454,8 +462,9 @@ void SDLHostInterface::LoadSettings()
|
|||
{
|
||||
// Settings need to be loaded prior to creating the window for OpenGL bits.
|
||||
m_settings_interface = std::make_unique<INISettingsInterface>(GetSettingsFileName());
|
||||
m_settings_copy.Load(*m_settings_interface);
|
||||
CommonHostInterface::LoadSettings(*m_settings_interface.get());
|
||||
m_settings_copy = g_settings;
|
||||
CommonHostInterface::FixIncompatibleSettings(false);
|
||||
}
|
||||
|
||||
void SDLHostInterface::ReportError(const char* message)
|
||||
|
@ -807,7 +816,7 @@ void SDLHostInterface::DrawQuickSettingsMenu()
|
|||
|
||||
if (ImGui::BeginMenu("CPU Execution Mode"))
|
||||
{
|
||||
const CPUExecutionMode current = g_settings.cpu_execution_mode;
|
||||
const CPUExecutionMode current = m_settings_copy.cpu_execution_mode;
|
||||
for (u32 i = 0; i < static_cast<u32>(CPUExecutionMode::Count); i++)
|
||||
{
|
||||
if (ImGui::MenuItem(Settings::GetCPUExecutionModeDisplayName(static_cast<CPUExecutionMode>(i)), nullptr,
|
||||
|
@ -874,6 +883,8 @@ void SDLHostInterface::DrawQuickSettingsMenu()
|
|||
&m_settings_copy.gpu_pgxp_texture_correction, m_settings_copy.gpu_pgxp_enable);
|
||||
settings_changed |= ImGui::MenuItem("PGXP Vertex Cache", nullptr, &m_settings_copy.gpu_pgxp_vertex_cache,
|
||||
m_settings_copy.gpu_pgxp_enable);
|
||||
settings_changed |=
|
||||
ImGui::MenuItem("PGXP CPU Instructions", nullptr, &m_settings_copy.gpu_pgxp_cpu, m_settings_copy.gpu_pgxp_enable);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
|
@ -1347,6 +1358,7 @@ void SDLHostInterface::DrawSettingsWindow()
|
|||
settings_changed |= ImGui::Checkbox("PGXP Culling", &m_settings_copy.gpu_pgxp_culling);
|
||||
settings_changed |= ImGui::Checkbox("PGXP Texture Correction", &m_settings_copy.gpu_pgxp_texture_correction);
|
||||
settings_changed |= ImGui::Checkbox("PGXP Vertex Cache", &m_settings_copy.gpu_pgxp_vertex_cache);
|
||||
settings_changed |= ImGui::Checkbox("PGXP CPU", &m_settings_copy.gpu_pgxp_cpu);
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
|
|
|
@ -71,6 +71,8 @@ bool CommonHostInterface::Initialize()
|
|||
m_game_list->SetCacheFilename(GetUserDirectoryRelativePath("cache/gamelist.cache"));
|
||||
m_game_list->SetDatabaseFilename(GetUserDirectoryRelativePath("cache/redump.dat"));
|
||||
m_game_list->SetCompatibilityFilename(GetProgramDirectoryRelativePath("database/compatibility.xml"));
|
||||
m_game_list->SetGameSettingsFilename(GetProgramDirectoryRelativePath("database/gamesettings.ini"));
|
||||
m_game_list->SetUserGameSettingsFilename(GetUserDirectoryRelativePath("gamesettings.ini"));
|
||||
|
||||
m_save_state_selector_ui = std::make_unique<FrontendCommon::SaveStateSelectorUI>(this);
|
||||
|
||||
|
@ -1238,44 +1240,51 @@ bool CommonHostInterface::AddRumbleToInputMap(const std::string& binding, u32 co
|
|||
|
||||
void CommonHostInterface::RegisterGeneralHotkeys()
|
||||
{
|
||||
RegisterHotkey(StaticString("General"), StaticString("FastForward"), StaticString("Fast Forward"),
|
||||
RegisterHotkey(StaticString("General"), StaticString("FastForward"), TRANSLATABLE("Hotkeys", "Fast Forward"),
|
||||
[this](bool pressed) {
|
||||
m_speed_limiter_temp_disabled = pressed;
|
||||
UpdateSpeedLimiterState();
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString("General"), StaticString("ToggleFastForward"), StaticString("Toggle Fast Forward"),
|
||||
[this](bool pressed) {
|
||||
RegisterHotkey(StaticString("General"), StaticString("ToggleFastForward"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle Fast Forward")), [this](bool pressed) {
|
||||
if (!pressed)
|
||||
{
|
||||
m_speed_limiter_temp_disabled = !m_speed_limiter_temp_disabled;
|
||||
UpdateSpeedLimiterState();
|
||||
AddFormattedOSDMessage(2.0f, "Speed limiter %s.",
|
||||
m_speed_limiter_enabled ? "enabled" : "disabled");
|
||||
AddOSDMessage(m_speed_limiter_enabled ?
|
||||
TranslateStdString("OSDMessage", "Speed limiter enabled.") :
|
||||
TranslateStdString("OSDMessage", "Speed limiter disabled."),
|
||||
2.0f);
|
||||
}
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString("General"), StaticString("ToggleFullscreen"), StaticString("Toggle Fullscreen"),
|
||||
[this](bool pressed) {
|
||||
RegisterHotkey(StaticString("General"), StaticString("ToggleFullscreen"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle Fullscreen")), [this](bool pressed) {
|
||||
if (!pressed)
|
||||
SetFullscreen(!IsFullscreen());
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString("General"), StaticString("TogglePause"), StaticString("Toggle Pause"),
|
||||
[this](bool pressed) {
|
||||
RegisterHotkey(StaticString("General"), StaticString("TogglePause"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle Pause")), [this](bool pressed) {
|
||||
if (System::IsValid() && !pressed)
|
||||
PauseSystem(!System::IsPaused());
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString("General"), StaticString("PowerOff"), StaticString("Power Off System"),
|
||||
[this](bool pressed) {
|
||||
RegisterHotkey(StaticString("General"), StaticString("PowerOff"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Power Off System")), [this](bool pressed) {
|
||||
if (!pressed && System::IsValid())
|
||||
{
|
||||
if (g_settings.confim_power_off && !m_batch_mode)
|
||||
{
|
||||
SmallString confirmation_message("Are you sure you want to stop emulation?");
|
||||
SmallString confirmation_message(
|
||||
TranslateString("CommonHostInterface", "Are you sure you want to stop emulation?"));
|
||||
if (g_settings.save_state_on_exit)
|
||||
confirmation_message.AppendString("\n\nThe current state will be saved.");
|
||||
{
|
||||
confirmation_message.AppendString("\n\n");
|
||||
confirmation_message.AppendString(
|
||||
TranslateString("CommonHostInterface", "The current state will be saved."));
|
||||
}
|
||||
|
||||
if (!ConfirmMessage(confirmation_message))
|
||||
{
|
||||
|
@ -1288,53 +1297,55 @@ void CommonHostInterface::RegisterGeneralHotkeys()
|
|||
}
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString("General"), StaticString("Screenshot"), StaticString("Save Screenshot"),
|
||||
[this](bool pressed) {
|
||||
RegisterHotkey(StaticString("General"), StaticString("Screenshot"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Save Screenshot")), [this](bool pressed) {
|
||||
if (!pressed && System::IsValid())
|
||||
SaveScreenshot();
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString("General"), StaticString("FrameStep"), StaticString("Frame Step"), [this](bool pressed) {
|
||||
if (!pressed)
|
||||
{
|
||||
DoFrameStep();
|
||||
}
|
||||
});
|
||||
RegisterHotkey(StaticString("General"), StaticString("FrameStep"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Frame Step")), [this](bool pressed) {
|
||||
if (!pressed)
|
||||
{
|
||||
DoFrameStep();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CommonHostInterface::RegisterGraphicsHotkeys()
|
||||
{
|
||||
RegisterHotkey(StaticString("Graphics"), StaticString("ToggleSoftwareRendering"),
|
||||
StaticString("Toggle Software Rendering"), [this](bool pressed) {
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle Software Rendering")), [this](bool pressed) {
|
||||
if (!pressed)
|
||||
ToggleSoftwareRendering();
|
||||
});
|
||||
|
||||
RegisterHotkey(
|
||||
StaticString("Graphics"), StaticString("TogglePGXP"), StaticString("Toggle PGXP"), [this](bool pressed) {
|
||||
if (!pressed)
|
||||
{
|
||||
g_settings.gpu_pgxp_enable = !g_settings.gpu_pgxp_enable;
|
||||
g_gpu->UpdateSettings();
|
||||
AddFormattedOSDMessage(5.0f, "PGXP is now %s.", g_settings.gpu_pgxp_enable ? "enabled" : "disabled");
|
||||
RegisterHotkey(StaticString("Graphics"), StaticString("TogglePGXP"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Toggle PGXP")), [this](bool pressed) {
|
||||
if (!pressed)
|
||||
{
|
||||
g_settings.gpu_pgxp_enable = !g_settings.gpu_pgxp_enable;
|
||||
g_gpu->UpdateSettings();
|
||||
AddFormattedOSDMessage(5.0f, "PGXP is now %s.",
|
||||
g_settings.gpu_pgxp_enable ? "enabled" : "disabled");
|
||||
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
PGXP::Initialize();
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
PGXP::Initialize();
|
||||
|
||||
// we need to recompile all blocks if pgxp is toggled on/off
|
||||
if (g_settings.IsUsingCodeCache())
|
||||
CPU::CodeCache::Flush();
|
||||
}
|
||||
});
|
||||
// we need to recompile all blocks if pgxp is toggled on/off
|
||||
if (g_settings.IsUsingCodeCache())
|
||||
CPU::CodeCache::Flush();
|
||||
}
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString("Graphics"), StaticString("IncreaseResolutionScale"),
|
||||
StaticString("Increase Resolution Scale"), [this](bool pressed) {
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Increase Resolution Scale")), [this](bool pressed) {
|
||||
if (!pressed)
|
||||
ModifyResolutionScale(1);
|
||||
});
|
||||
|
||||
RegisterHotkey(StaticString("Graphics"), StaticString("DecreaseResolutionScale"),
|
||||
StaticString("Decrease Resolution Scale"), [this](bool pressed) {
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Decrease Resolution Scale")), [this](bool pressed) {
|
||||
if (!pressed)
|
||||
ModifyResolutionScale(-1);
|
||||
});
|
||||
|
@ -1343,80 +1354,94 @@ void CommonHostInterface::RegisterGraphicsHotkeys()
|
|||
void CommonHostInterface::RegisterSaveStateHotkeys()
|
||||
{
|
||||
RegisterHotkey(StaticString("Save States"), StaticString("LoadSelectedSaveState"),
|
||||
StaticString("Load From Selected Slot"), [this](bool pressed) {
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Load From Selected Slot")), [this](bool pressed) {
|
||||
if (!pressed)
|
||||
m_save_state_selector_ui->LoadCurrentSlot();
|
||||
});
|
||||
RegisterHotkey(StaticString("Save States"), StaticString("SaveSelectedSaveState"),
|
||||
StaticString("Save To Selected Slot"), [this](bool pressed) {
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Save To Selected Slot")), [this](bool pressed) {
|
||||
if (!pressed)
|
||||
m_save_state_selector_ui->SaveCurrentSlot();
|
||||
});
|
||||
RegisterHotkey(StaticString("Save States"), StaticString("SelectPreviousSaveStateSlot"),
|
||||
StaticString("Select Previous Save Slot"), [this](bool pressed) {
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Select Previous Save Slot")), [this](bool pressed) {
|
||||
if (!pressed)
|
||||
m_save_state_selector_ui->SelectPreviousSlot();
|
||||
});
|
||||
RegisterHotkey(StaticString("Save States"), StaticString("SelectNextSaveStateSlot"),
|
||||
StaticString("Select Next Save Slot"), [this](bool pressed) {
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Select Next Save Slot")), [this](bool pressed) {
|
||||
if (!pressed)
|
||||
m_save_state_selector_ui->SelectNextSlot();
|
||||
});
|
||||
|
||||
for (u32 global_i = 0; global_i < 2; global_i++)
|
||||
for (u32 slot = 1; slot <= PER_GAME_SAVE_STATE_SLOTS; slot++)
|
||||
{
|
||||
const bool global = ConvertToBoolUnchecked(global_i);
|
||||
const u32 count = global ? GLOBAL_SAVE_STATE_SLOTS : PER_GAME_SAVE_STATE_SLOTS;
|
||||
for (u32 slot = 1; slot <= count; slot++)
|
||||
{
|
||||
RegisterHotkey(StaticString("Save States"),
|
||||
TinyString::FromFormat("Load%sState%u", global ? "Global" : "Game", slot),
|
||||
TinyString::FromFormat("Load %s State %u", global ? "Global" : "Game", slot),
|
||||
[this, global, slot](bool pressed) {
|
||||
if (!pressed)
|
||||
LoadState(global, slot);
|
||||
});
|
||||
RegisterHotkey(StaticString("Save States"),
|
||||
TinyString::FromFormat("Save%sState%u", global ? "Global" : "Game", slot),
|
||||
TinyString::FromFormat("Save %s State %u", global ? "Global" : "Game", slot),
|
||||
[this, global, slot](bool pressed) {
|
||||
if (!pressed)
|
||||
SaveState(global, slot);
|
||||
});
|
||||
}
|
||||
RegisterHotkey(StaticString("Save States"), TinyString::FromFormat("LoadGameState%u", slot),
|
||||
TinyString::FromFormat(TRANSLATABLE("Hotkeys", "Load Game State %u"), slot),
|
||||
[this, slot](bool pressed) {
|
||||
if (!pressed)
|
||||
LoadState(false, slot);
|
||||
});
|
||||
RegisterHotkey(StaticString("Save States"), TinyString::FromFormat("SaveGameState%u", slot),
|
||||
TinyString::FromFormat(TRANSLATABLE("Hotkeys", "Save Game State %u"), slot),
|
||||
[this, slot](bool pressed) {
|
||||
if (!pressed)
|
||||
SaveState(false, slot);
|
||||
});
|
||||
}
|
||||
|
||||
for (u32 slot = 1; slot <= GLOBAL_SAVE_STATE_SLOTS; slot++)
|
||||
{
|
||||
RegisterHotkey(StaticString("Save States"), TinyString::FromFormat("LoadGlobalState%u", slot),
|
||||
TinyString::FromFormat(TRANSLATABLE("Hotkeys", "Load Global State %u"), slot),
|
||||
[this, slot](bool pressed) {
|
||||
if (!pressed)
|
||||
LoadState(true, slot);
|
||||
});
|
||||
RegisterHotkey(StaticString("Save States"), TinyString::FromFormat("SaveGlobalState%u", slot),
|
||||
TinyString::FromFormat(TRANSLATABLE("Hotkeys", "Save Global State %u"), slot),
|
||||
[this, slot](bool pressed) {
|
||||
if (!pressed)
|
||||
SaveState(true, slot);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void CommonHostInterface::RegisterAudioHotkeys()
|
||||
{
|
||||
RegisterHotkey(StaticString("Audio"), StaticString("AudioMute"), StaticString("Toggle Mute"), [this](bool pressed) {
|
||||
if (System::IsValid() && !pressed)
|
||||
{
|
||||
g_settings.audio_output_muted = !g_settings.audio_output_muted;
|
||||
m_audio_stream->SetOutputVolume(g_settings.audio_output_muted ? 0 : g_settings.audio_output_volume);
|
||||
if (g_settings.audio_output_muted)
|
||||
AddOSDMessage("Volume: Muted", 2.0f);
|
||||
else
|
||||
AddFormattedOSDMessage(2.0f, "Volume: %d%%", g_settings.audio_output_volume);
|
||||
}
|
||||
});
|
||||
RegisterHotkey(StaticString("Audio"), StaticString("AudioVolumeUp"), StaticString("Volume Up"), [this](bool pressed) {
|
||||
if (System::IsValid() && pressed)
|
||||
{
|
||||
g_settings.audio_output_volume = std::min<s32>(g_settings.audio_output_volume + 10, 100);
|
||||
g_settings.audio_output_muted = false;
|
||||
m_audio_stream->SetOutputVolume(g_settings.audio_output_volume);
|
||||
AddFormattedOSDMessage(2.0f, "Volume: %d%%", g_settings.audio_output_volume);
|
||||
}
|
||||
});
|
||||
RegisterHotkey(StaticString("Audio"), StaticString("AudioVolumeDown"), StaticString("Volume Down"),
|
||||
[this](bool pressed) {
|
||||
RegisterHotkey(
|
||||
StaticString("Audio"), StaticString("AudioMute"), StaticString(TRANSLATABLE("Hotkeys", "Toggle Mute")),
|
||||
[this](bool pressed) {
|
||||
if (System::IsValid() && !pressed)
|
||||
{
|
||||
g_settings.audio_output_muted = !g_settings.audio_output_muted;
|
||||
m_audio_stream->SetOutputVolume(g_settings.audio_output_muted ? 0 : g_settings.audio_output_volume);
|
||||
if (g_settings.audio_output_muted)
|
||||
AddOSDMessage(TranslateStdString("OSDMessage", "Volume: Muted"), 2.0f);
|
||||
else
|
||||
AddFormattedOSDMessage(2.0f, TranslateString("OSDMessage", "Volume: %d%%"), g_settings.audio_output_volume);
|
||||
}
|
||||
});
|
||||
RegisterHotkey(StaticString("Audio"), StaticString("AudioVolumeUp"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Volume Up")), [this](bool pressed) {
|
||||
if (System::IsValid() && pressed)
|
||||
{
|
||||
g_settings.audio_output_volume = std::min<s32>(g_settings.audio_output_volume + 10, 100);
|
||||
g_settings.audio_output_muted = false;
|
||||
m_audio_stream->SetOutputVolume(g_settings.audio_output_volume);
|
||||
AddFormattedOSDMessage(2.0f, TranslateString("OSDMessage", "Volume: %d%%"),
|
||||
g_settings.audio_output_volume);
|
||||
}
|
||||
});
|
||||
RegisterHotkey(StaticString("Audio"), StaticString("AudioVolumeDown"),
|
||||
StaticString(TRANSLATABLE("Hotkeys", "Volume Down")), [this](bool pressed) {
|
||||
if (System::IsValid() && pressed)
|
||||
{
|
||||
g_settings.audio_output_volume = std::max<s32>(g_settings.audio_output_volume - 10, 0);
|
||||
g_settings.audio_output_muted = false;
|
||||
m_audio_stream->SetOutputVolume(g_settings.audio_output_volume);
|
||||
AddFormattedOSDMessage(2.0f, "Volume: %d%%", g_settings.audio_output_volume);
|
||||
AddFormattedOSDMessage(2.0f, TranslateString("OSDMessage", "Volume: %d%%"),
|
||||
g_settings.audio_output_volume);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1552,7 +1577,7 @@ void CommonHostInterface::ApplyInputProfile(const char* profile_path, SettingsIn
|
|||
|
||||
UpdateInputMap(si);
|
||||
|
||||
ReportFormattedMessage("Loaded input profile from '%s'", profile_path);
|
||||
ReportFormattedMessage(TranslateString("OSDMessage", "Loaded input profile from '%s'"), profile_path);
|
||||
}
|
||||
|
||||
bool CommonHostInterface::SaveInputProfile(const char* profile_path, SettingsInterface& si)
|
||||
|
@ -2016,14 +2041,25 @@ bool CommonHostInterface::SaveScreenshot(const char* filename /* = nullptr */, b
|
|||
const bool screenshot_saved = m_display->WriteDisplayTextureToFile(filename, full_resolution, apply_aspect_ratio);
|
||||
if (!screenshot_saved)
|
||||
{
|
||||
AddFormattedOSDMessage(10.0f, "Failed to save screenshot to '%s'", filename);
|
||||
AddFormattedOSDMessage(10.0f, TranslateString("OSDMessage", "Failed to save screenshot to '%s'"), filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
AddFormattedOSDMessage(5.0f, "Screenshot saved to '%s'.", filename);
|
||||
AddFormattedOSDMessage(5.0f, TranslateString("OSDMessage", "Screenshot saved to '%s'."), filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CommonHostInterface::ApplyGameSettings(bool display_osd_messages)
|
||||
{
|
||||
// this gets called while booting, so can't use valid
|
||||
if (System::IsShutdown() || System::GetRunningCode().empty() || !g_settings.apply_game_settings)
|
||||
return;
|
||||
|
||||
const GameSettings::Entry* gs = m_game_list->GetGameSettings(System::GetRunningPath(), System::GetRunningCode());
|
||||
if (gs)
|
||||
gs->ApplySettings(display_osd_messages);
|
||||
}
|
||||
|
||||
#ifdef WITH_DISCORD_PRESENCE
|
||||
|
||||
void CommonHostInterface::SetDiscordPresenceEnabled(bool enabled)
|
||||
|
|
|
@ -266,6 +266,8 @@ protected:
|
|||
|
||||
void RecreateSystem() override;
|
||||
|
||||
void ApplyGameSettings(bool display_osd_messages);
|
||||
|
||||
virtual void DrawImGuiWindows();
|
||||
|
||||
void DrawFPSWindow();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "sdl_controller_interface.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "core/controller.h"
|
||||
#include "core/host_interface.h"
|
||||
|
@ -23,6 +24,17 @@ bool SDLControllerInterface::Initialize(CommonHostInterface* host_interface)
|
|||
|
||||
FrontendCommon::EnsureSDLInitialized();
|
||||
|
||||
const std::string gcdb_file_name = GetGameControllerDBFileName();
|
||||
if (FileSystem::FileExists(gcdb_file_name.c_str()))
|
||||
{
|
||||
Log_InfoPrintf("Loading game controller mappings from '%s'", gcdb_file_name.c_str());
|
||||
if (SDL_GameControllerAddMappingsFromFile(gcdb_file_name.c_str()) < 0)
|
||||
{
|
||||
Log_ErrorPrintf("SDL_GameControllerAddMappingsFromFile(%s) failed: %s",
|
||||
gcdb_file_name.c_str(), SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) < 0)
|
||||
{
|
||||
Log_ErrorPrintf("SDL_InitSubSystem(SDL_INIT_JOYSTICK |SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) failed");
|
||||
|
@ -48,6 +60,11 @@ void SDLControllerInterface::Shutdown()
|
|||
ControllerInterface::Shutdown();
|
||||
}
|
||||
|
||||
std::string SDLControllerInterface::GetGameControllerDBFileName() const
|
||||
{
|
||||
return m_host_interface->GetUserDirectoryRelativePath("gamecontrollerdb.txt");
|
||||
}
|
||||
|
||||
void SDLControllerInterface::PollEvents()
|
||||
{
|
||||
for (;;)
|
||||
|
|
|
@ -17,6 +17,9 @@ public:
|
|||
bool Initialize(CommonHostInterface* host_interface) override;
|
||||
void Shutdown() override;
|
||||
|
||||
/// Returns the path of the optional game controller database file.
|
||||
std::string GetGameControllerDBFileName() const;
|
||||
|
||||
// Removes all bindings. Call before setting new bindings.
|
||||
void ClearBindings() override;
|
||||
|
||||
|
|
|
@ -4,8 +4,9 @@ SET VERSIONFILE="scmversion.cpp"
|
|||
FOR /F "tokens=* USEBACKQ" %%g IN (`git rev-parse HEAD`) do (SET "HASH=%%g")
|
||||
FOR /F "tokens=* USEBACKQ" %%g IN (`git rev-parse --abbrev-ref HEAD`) do (SET "BRANCH=%%g")
|
||||
FOR /F "tokens=* USEBACKQ" %%g IN (`git describe --tags --dirty --exclude latest`) do (SET "TAG=%%g")
|
||||
FOR /F "tokens=* USEBACKQ" %%g IN (`git log -1 --date=iso8601-strict "--format=%%cd"`) do (SET "CDATE=%%g")
|
||||
|
||||
SET SIGNATURELINE=// %HASH% %BRANCH% %TAG%
|
||||
SET SIGNATURELINE=// %HASH% %BRANCH% %TAG% %CDATE%
|
||||
SET /P EXISTINGLINE=< %VERSIONFILE%
|
||||
|
||||
IF "%EXISTINGLINE%"=="%SIGNATURELINE%" (
|
||||
|
@ -19,6 +20,7 @@ ECHO Updating %VERSIONFILE%...
|
|||
ECHO const char* g_scm_hash_str = "%HASH%";
|
||||
ECHO const char* g_scm_branch_str = "%BRANCH%";
|
||||
ECHO const char* g_scm_tag_str = "%TAG%";
|
||||
ECHO const char* g_scm_date_str = "%CDATE%";
|
||||
)>%VERSIONFILE%
|
||||
|
||||
EXIT
|
||||
|
|
|
@ -4,8 +4,9 @@ VERSION_FILE="scmversion.cpp"
|
|||
HASH=$(git rev-parse HEAD)
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD | tr -d '\r\n')
|
||||
TAG=$(git describe --tags --dirty --exclude latest | tr -d '\r\n')
|
||||
DATE=$(git log -1 --date=iso8601-strict --format=%cd)
|
||||
|
||||
SIGNATURE_LINE="// ${HASH} ${BRANCH} ${TAG}"
|
||||
SIGNATURE_LINE="// ${HASH} ${BRANCH} ${TAG} ${DATE}"
|
||||
|
||||
if [ -f $VERSION_FILE ]; then
|
||||
EXISTING_LINE=$(head -n1 $VERSION_FILE | tr -d '\n')
|
||||
|
@ -22,6 +23,7 @@ ${SIGNATURE_LINE}
|
|||
const char* g_scm_hash_str = "${HASH}";
|
||||
const char* g_scm_branch_str = "${BRANCH}";
|
||||
const char* g_scm_tag_str = "${TAG}";
|
||||
const char* g_scm_date_str = "${DATE}";
|
||||
|
||||
EOF
|
||||
|
||||
|
|
|
@ -3,4 +3,5 @@
|
|||
extern const char* g_scm_hash_str;
|
||||
extern const char* g_scm_branch_str;
|
||||
extern const char* g_scm_tag_str;
|
||||
extern const char* g_scm_date_str;
|
||||
|
||||
|
|
Loading…
Reference in a new issue