Merge remote-tracking branch 'origin/main'

This commit is contained in:
kalaposfos13 2025-01-30 20:24:09 +01:00
commit 9124e3d536
176 changed files with 6414 additions and 1915 deletions

View File

@ -120,7 +120,7 @@ find_package(SDL3 3.1.2 CONFIG)
find_package(stb MODULE)
find_package(toml11 4.2.0 CONFIG)
find_package(tsl-robin-map 1.3.0 CONFIG)
find_package(VulkanHeaders 1.4.303 CONFIG)
find_package(VulkanHeaders 1.4.305 CONFIG)
find_package(VulkanMemoryAllocator 3.1.0 CONFIG)
find_package(xbyak 7.07 CONFIG)
find_package(xxHash 0.8.2 MODULE)
@ -344,6 +344,8 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
src/core/libraries/razor_cpu/razor_cpu.h
src/core/libraries/mouse/mouse.cpp
src/core/libraries/mouse/mouse.h
src/core/libraries/web_browser_dialog/webbrowserdialog.cpp
src/core/libraries/web_browser_dialog/webbrowserdialog.h
)
set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h
@ -434,6 +436,13 @@ set(NP_LIBS src/core/libraries/np_common/np_common.cpp
src/core/libraries/np_trophy/np_trophy_error.h
src/core/libraries/np_web_api/np_web_api.cpp
src/core/libraries/np_web_api/np_web_api.h
src/core/libraries/np_party/np_party.cpp
src/core/libraries/np_party/np_party.h
)
set(ZLIB_LIB src/core/libraries/zlib/zlib.cpp
src/core/libraries/zlib/zlib_sce.h
src/core/libraries/zlib/zlib_error.h
)
set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp
@ -557,6 +566,16 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/devices/logger.cpp
src/core/devices/logger.h
src/core/devices/nop_device.h
src/core/devices/console_device.cpp
src/core/devices/console_device.h
src/core/devices/deci_tty6_device.cpp
src/core/devices/deci_tty6_device.h
src/core/devices/random_device.cpp
src/core/devices/random_device.h
src/core/devices/urandom_device.cpp
src/core/devices/urandom_device.h
src/core/devices/srandom_device.cpp
src/core/devices/srandom_device.h
src/core/file_format/pfs.h
src/core/file_format/pkg.cpp
src/core/file_format/pkg.h
@ -598,6 +617,7 @@ set(CORE src/core/aerolib/stubs.cpp
${PLAYGO_LIB}
${RANDOM_LIB}
${USBD_LIB}
${ZLIB_LIB}
${MISC_LIBS}
${IME_LIB}
${FIBER_LIB}
@ -741,8 +761,6 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
src/video_core/renderer_vulkan/vk_common.h
src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
src/video_core/renderer_vulkan/vk_compute_pipeline.h
src/video_core/renderer_vulkan/vk_descriptor_update_queue.cpp
src/video_core/renderer_vulkan/vk_descriptor_update_queue.h
src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
src/video_core/renderer_vulkan/vk_graphics_pipeline.h
src/video_core/renderer_vulkan/vk_instance.cpp

View File

@ -55,6 +55,9 @@ This project began as a fun project. Given our limited free time, it may take so
# Building
> [!IMPORTANT]
> If you want to use shadPS4 to play your games, you don't have to follow the build instructions, you can simply download the emulator from either the [**release tab**](https://github.com/shadps4-emu/shadPS4/releases) or the [**action tab**](https://github.com/shadps4-emu/shadPS4/actions).
## Windows
Check the build instructions for [**Windows**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-windows.md).
@ -76,6 +79,9 @@ For more information on how to test, debug and report issues with the emulator o
# Keyboard and Mouse Mappings
> [!NOTE]
> Some keyboards may also require you to hold the Fn key to use the F\* keys. Mac users should use the Command key instead of Control, and need to use Command+F11 for full screen to avoid conflicting with system key bindings.
| Button | Function |
|-------------|-------------|
F10 | FPS Counter

View File

@ -14,6 +14,7 @@ path = [
"documents/changelog.md",
"documents/Quickstart/2.png",
"documents/Screenshots/*",
"documents/Screenshots/Linux/*",
"externals/MoltenVK/MoltenVK_icd.json",
"scripts/ps4_names.txt",
"src/images/about_icon.png",

View File

@ -22,7 +22,10 @@ SPDX-License-Identifier: GPL-2.0-or-later
- A processor with at least 4 cores and 6 threads
- Above 2.5 GHz frequency
- required support AVX2 extension or Rosetta 2 on ARM
- A CPU supporting the following instruction sets: MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, F16C, CLMUL, AES, BMI1, MOVBE, XSAVE, ABM
- **Intel**: Haswell generation or newer
- **AMD**: Jaguar generation or newer
- **Apple**: Rosetta 2 on macOS 15 or newer
### GPU

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -5,57 +5,132 @@ SPDX-License-Identifier: GPL-2.0-or-later
## Build shadPS4 for Linux
### Install the necessary tools to build shadPS4:
First and foremost, Clang 18 is the **recommended compiler** as it is used for official builds and CI. If you build with GCC, you might encounter issues — please report any you find. Additionally, if you choose to use GCC, please build shadPS4 with Clang at least once before creating an `[APP BUG]` issue or submitting a pull request.
## Preparatory steps
### Installing dependencies
#### Debian & Ubuntu
```
sudo apt install build-essential clang git cmake libasound2-dev libpulse-dev libopenal-dev libssl-dev zlib1g-dev libedit-dev libudev-dev libevdev-dev libsdl2-dev libjack-dev libsndio-dev qt6-base-dev qt6-tools-dev qt6-multimedia-dev libvulkan-dev vulkan-validationlayers
```
#### Fedora
```
sudo dnf install clang git cmake libatomic alsa-lib-devel pipewire-jack-audio-connection-kit-devel openal-devel openssl-devel libevdev-devel libudev-devel libXext-devel qt6-qtbase-devel qt6-qtbase-private-devel qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel vulkan-devel vulkan-validation-layers
```
#### Arch Linux
```
sudo pacman -S base-devel clang git cmake sndio jack2 openal qt6-base qt6-declarative qt6-multimedia sdl2 vulkan-validation-layers
```
**Note**: The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion.
#### OpenSUSE
```
sudo zypper install clang git cmake libasound2 libpulse-devel libsndio7 libjack-devel openal-soft-devel libopenssl-devel zlib-devel libedit-devel systemd-devel libevdev-devel qt6-base-devel qt6-multimedia-devel qt6-svg-devel qt6-linguist-devel qt6-gui-private-devel vulkan-devel vulkan-validationlayers
```
### Cloning and compiling:
Clone the repository recursively:
#### Other Linux distributions
You can try one of two methods:
- Search the packages by name and install them with your package manager, or
- Install [distrobox](https://distrobox.it/), create a container using any of the distributions cited above as a base, for Arch Linux you'd do:
```
distrobox create --name archlinux --init --image archlinux:latest
```
and install the dependencies on that container as cited above.
This option is **highly recommended** for NixOS and distributions with immutable/atomic filesystems (example: Fedora Kinoite, SteamOS).
### Cloning
```
git clone --recursive https://github.com/shadps4-emu/shadPS4.git
cd shadPS4
```
Generate the build directory in the shadPS4 directory. To disable the QT GUI, remove the ```-DENABLE_QT_GUI=ON``` flag:
## Building
There are 3 options you can choose from. Option 1 is **highly recommended**.
#### Option 1: Terminal-only
1. Generate the build directory in the shadPS4 directory.
**Note**: Clang is the compiler used for official builds and CI. If you build with GCC, you might encounter issues—please report any you find. If you choose to use GCC, we recommend building with Clang at least once before submitting a pull request.
```
cmake -S . -B build/ -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
```
Enter the directory:
To disable the Qt GUI, remove the `-DENABLE_QT_GUI=ON` flag. To change the build type (for debugging), add `-DCMAKE_BUILD_TYPE=Debug`.
2. Use CMake to build the project:
```
cd build/
cmake --build ./build --parallel$(nproc)
```
Use make to build the project:
If your computer freezes during this step, this could be caused by excessive system resource usage. In that case, remove `--parallel$(nproc)`.
Now run the emulator. If Qt was enabled at configure time:
```
cmake --build . --parallel$(nproc)
./build/shadps4
```
Now run the emulator. If QT is enabled:
```
./shadps4
```
Otherwise, specify the path to your PKG's boot file:
```
./shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin
./build/shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin
```
You can also specify the Game ID as an argument for which game to boot, as long as the folder containing the games is specified in config.toml (example: Bloodborne (US) is CUSA00900).
#### Option 2: Configuring with cmake-gui
`cmake-gui` should be installed by default alongside `cmake`, if not search for the package in your package manager and install it.
Open `cmake-gui` and specify the source code and build directories. If you cloned the source code to your Home directory, it would be `/home/user/shadPS4` and `/home/user/shadPS4/build`.
Click on Configure, select "Unix Makefiles", select "Specify native compilers", click Next and choose `clang` and `clang++` as the C and CXX compilers. Usually they are located in `/bin/clang` and `/bin/clang++`. Click on Finish and let it configure the project.
Now every option should be displayed in red. Change anything you want, then click on Generate to make the changes permanent, then open a terminal window and do step 2 of Option 1.
#### Option 3: Visual Studio Code
This option is pretty convoluted and should only be used if you have VSCode as your default IDE, or just prefer building and debugging projects through it. This also assumes that you're using an Arch Linux environment, as the naming for some options might differ from other distros.
[Download Visual Studio Code for your platform](https://code.visualstudio.com/download), or use [Code - OSS](https://github.com/microsoft/vscode) if you'd like. Code - OSS is available on most Linux distributions' package repositories (on Arch Linux it is simply named `code`).
Once set up, go to Extensions and install "CMake Tools":
![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/3.png)
You can also install other CMake and Clang related extensions if you'd like, but this one is what enables you to configure and build CMake projects directly within VSCode.
Go to Settings, filter by `@ext:ms-vscode.cmake-tools configure` and disable this option:
![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/1.png)
If you wish to build with the Qt GUI, add `-DENABLE_QT_GUI=ON` to the configure arguments:
![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/2.png)
On the CMake tab, change the options as you wish, but make sure that it looks similar to or exactly like this:
![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/4.png)
When hovering over Project Status > Configure, there should be an icon titled "Configure". Click on it and let it configure the project, then do the same for Project Status > Build.
If you want to debug it, change the build type under Project Status > Configure to Debug (it should be the default) and compile it, then click on the icon in Project Status > Debug. If you simply want to launch the shadPS4 executable from within VSCode, click on the icon in Project Status > Launch.
Don't forget to change the launch target for both options to the shadPS4 executable inside shadPS4/build:
![image](https://raw.githubusercontent.com/shadps4-emu/shadPS4/refs/heads/main/documents/Screenshots/Linux/5.png)

@ -1 +1 @@
Subproject commit 9f0b616d9e2c39464d2a859b79dbc655c4a30e7e
Subproject commit 0c090001cb42997031cfe43914340e2639944972

@ -1 +1 @@
Subproject commit 6173e24b31f09a0c3217103a130e74c4ddec14a6
Subproject commit 1a7b7ef6de02cf6767e42b10ddad217c45e90d47

2
externals/sdl3 vendored

@ -1 +1 @@
Subproject commit 22422f7748d5128135995ed34c8f8012861c7332
Subproject commit a336b62d8b0b97b09214e053203e442e2b6e2be5

2
externals/sirit vendored

@ -1 +1 @@
Subproject commit 26ad5a9d0fe13260b0d7d6c64419d01a196b2e32
Subproject commit d6f3c0d99862ab2ff8f95e9ac221560f1f97e29a

@ -1 +1 @@
Subproject commit 6a74a7d65cafa19e38ec116651436cce6efd5b2e
Subproject commit a03d2f6d5753b365d704d58161825890baad0755

View File

@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifdef __linux__
#include <pthread.h>
#endif
namespace Common {
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
class AdaptiveMutex {
public:
void lock() {
pthread_mutex_lock(&mutex);
}
void unlock() {
pthread_mutex_unlock(&mutex);
}
private:
pthread_mutex_t mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;
};
#endif // PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
} // namespace Common

View File

@ -45,6 +45,9 @@ static std::string logFilter;
static std::string logType = "async";
static std::string userName = "shadPS4";
static std::string updateChannel;
static std::string chooseHomeTab;
static u16 deadZoneLeft = 2.0;
static u16 deadZoneRight = 2.0;
static std::string backButtonBehavior = "left";
static bool useSpecialPad = false;
static int specialPadClass = 1;
@ -76,6 +79,7 @@ static std::string trophyKey;
static bool load_game_size = true;
std::vector<std::filesystem::path> settings_install_dirs = {};
std::filesystem::path settings_addon_install_dir = {};
std::filesystem::path save_data_path = {};
u32 main_window_geometry_x = 400;
u32 main_window_geometry_y = 400;
u32 main_window_geometry_w = 1280;
@ -108,6 +112,13 @@ bool GetLoadGameSizeEnabled() {
return load_game_size;
}
std::filesystem::path GetSaveDataPath() {
if (save_data_path.empty()) {
return Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir);
}
return save_data_path;
}
void setLoadGameSizeEnabled(bool enable) {
load_game_size = enable;
}
@ -140,6 +151,14 @@ bool getEnableDiscordRPC() {
return enableDiscordRPC;
}
u16 leftDeadZone() {
return deadZoneLeft;
}
u16 rightDeadZone() {
return deadZoneRight;
}
s16 getCursorState() {
return cursorState;
}
@ -176,6 +195,10 @@ std::string getUpdateChannel() {
return updateChannel;
}
std::string getChooseHomeTab() {
return chooseHomeTab;
}
std::string getBackButtonBehavior() {
return backButtonBehavior;
}
@ -244,18 +267,28 @@ bool vkValidationGpuEnabled() {
return vkValidationGpu;
}
bool vkCrashDiagnosticEnabled() {
bool getVkCrashDiagnosticEnabled() {
return vkCrashDiagnostic;
}
bool vkHostMarkersEnabled() {
// Forced on when crash diagnostic enabled.
return vkHostMarkers || vkCrashDiagnostic;
bool getVkHostMarkersEnabled() {
return vkHostMarkers;
}
bool vkGuestMarkersEnabled() {
// Forced on when crash diagnostic enabled.
return vkGuestMarkers || vkCrashDiagnostic;
bool getVkGuestMarkersEnabled() {
return vkGuestMarkers;
}
void setVkCrashDiagnosticEnabled(bool enable) {
vkCrashDiagnostic = enable;
}
void setVkHostMarkersEnabled(bool enable) {
vkHostMarkers = enable;
}
void setVkGuestMarkersEnabled(bool enable) {
vkGuestMarkers = enable;
}
bool getSeparateUpdateEnabled() {
@ -381,6 +414,9 @@ void setUserName(const std::string& type) {
void setUpdateChannel(const std::string& type) {
updateChannel = type;
}
void setChooseHomeTab(const std::string& type) {
chooseHomeTab = type;
}
void setBackButtonBehavior(const std::string& type) {
backButtonBehavior = type;
@ -492,6 +528,10 @@ void setGameInstallDirs(const std::vector<std::filesystem::path>& settings_insta
settings_install_dirs = settings_install_dirs_config;
}
void setSaveDataPath(const std::filesystem::path& path) {
save_data_path = path;
}
u32 getMainWindowGeometryX() {
return main_window_geometry_x;
}
@ -615,11 +655,14 @@ void load(const std::filesystem::path& path) {
compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", false);
checkCompatibilityOnStartup =
toml::find_or<bool>(general, "checkCompatibilityOnStartup", false);
chooseHomeTab = toml::find_or<std::string>(general, "chooseHomeTab", "Release");
}
if (data.contains("Input")) {
const toml::value& input = data.at("Input");
deadZoneLeft = toml::find_or<float>(input, "deadZoneLeft", 2.0);
deadZoneRight = toml::find_or<float>(input, "deadZoneRight", 2.0);
cursorState = toml::find_or<int>(input, "cursorState", HideCursorState::Idle);
cursorHideTimeout = toml::find_or<int>(input, "cursorHideTimeout", 5);
backButtonBehavior = toml::find_or<std::string>(input, "backButtonBehavior", "left");
@ -678,6 +721,8 @@ void load(const std::filesystem::path& path) {
addGameInstallDir(std::filesystem::path{dir});
}
save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {});
settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {});
main_window_geometry_x = toml::find_or<int>(gui, "geometry_x", 0);
main_window_geometry_y = toml::find_or<int>(gui, "geometry_y", 0);
@ -734,11 +779,14 @@ void save(const std::filesystem::path& path) {
data["General"]["logType"] = logType;
data["General"]["userName"] = userName;
data["General"]["updateChannel"] = updateChannel;
data["General"]["chooseHomeTab"] = chooseHomeTab;
data["General"]["showSplash"] = isShowSplash;
data["General"]["autoUpdate"] = isAutoUpdate;
data["General"]["separateUpdateEnabled"] = separateupdatefolder;
data["General"]["compatibilityEnabled"] = compatibilityData;
data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup;
data["Input"]["deadZoneLeft"] = deadZoneLeft;
data["Input"]["deadZoneRight"] = deadZoneRight;
data["Input"]["cursorState"] = cursorState;
data["Input"]["cursorHideTimeout"] = cursorHideTimeout;
data["Input"]["backButtonBehavior"] = backButtonBehavior;
@ -770,6 +818,7 @@ void save(const std::filesystem::path& path) {
install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data});
}
data["GUI"]["installDirs"] = install_dirs;
data["GUI"]["saveDataPath"] = std::string{fmt::UTF(save_data_path.u8string()).data};
data["GUI"]["loadGameSizeEnabled"] = load_game_size;
data["GUI"]["addonInstallDir"] =
@ -842,6 +891,7 @@ void setDefaultValues() {
} else {
updateChannel = "Nightly";
}
chooseHomeTab = "General";
cursorState = HideCursorState::Idle;
cursorHideTimeout = 5;
backButtonBehavior = "left";

View File

@ -18,6 +18,7 @@ void saveMainWindow(const std::filesystem::path& path);
std::string getTrophyKey();
void setTrophyKey(std::string key);
bool GetLoadGameSizeEnabled();
std::filesystem::path GetSaveDataPath();
void setLoadGameSizeEnabled(bool enable);
bool getIsFullscreen();
std::string getFullscreenMode();
@ -34,7 +35,10 @@ std::string getLogFilter();
std::string getLogType();
std::string getUserName();
std::string getUpdateChannel();
std::string getChooseHomeTab();
u16 leftDeadZone();
u16 rightDeadZone();
s16 getCursorState();
int getCursorHideTimeout();
std::string getBackButtonBehavior();
@ -78,8 +82,10 @@ void setLanguage(u32 language);
void setNeoMode(bool enable);
void setUserName(const std::string& type);
void setUpdateChannel(const std::string& type);
void setChooseHomeTab(const std::string& type);
void setSeparateUpdateEnabled(bool use);
void setGameInstallDirs(const std::vector<std::filesystem::path>& settings_install_dirs_config);
void setSaveDataPath(const std::filesystem::path& path);
void setCompatibilityEnabled(bool use);
void setCheckCompatibilityOnStartup(bool use);
@ -100,9 +106,12 @@ void setRdocEnabled(bool enable);
bool vkValidationEnabled();
bool vkValidationSyncEnabled();
bool vkValidationGpuEnabled();
bool vkCrashDiagnosticEnabled();
bool vkHostMarkersEnabled();
bool vkGuestMarkersEnabled();
bool getVkCrashDiagnosticEnabled();
bool getVkHostMarkersEnabled();
bool getVkGuestMarkersEnabled();
void setVkCrashDiagnosticEnabled(bool enable);
void setVkHostMarkersEnabled(bool enable);
void setVkGuestMarkersEnabled(bool enable);
// Gui
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h);

View File

@ -80,6 +80,7 @@ public:
static constexpr u32 FW_40 = 0x4000000;
static constexpr u32 FW_45 = 0x4500000;
static constexpr u32 FW_50 = 0x5000000;
static constexpr u32 FW_55 = 0x5500000;
static constexpr u32 FW_80 = 0x8000000;
static ElfInfo& Instance() {

View File

@ -131,6 +131,9 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, Videodec) \
SUB(Lib, RazorCpu) \
SUB(Lib, Mouse) \
SUB(Lib, WebBrowserDialog) \
SUB(Lib, NpParty) \
SUB(Lib, Zlib) \
CLS(Frontend) \
CLS(Render) \
SUB(Render, Vulkan) \

View File

@ -98,6 +98,9 @@ enum class Class : u8 {
Lib_Videodec, ///< The LibSceVideodec implementation.
Lib_RazorCpu, ///< The LibRazorCpu implementation.
Lib_Mouse, ///< The LibSceMouse implementation
Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation
Lib_NpParty, ///< The LibSceNpParty implementation
Lib_Zlib, ///< The LibSceZlib implementation.
Frontend, ///< Emulator UI
Render, ///< Video Core
Render_Vulkan, ///< Vulkan backend

View File

@ -7,6 +7,7 @@
#include <string>
#include <pugixml.hpp>
#ifdef ENABLE_QT_GUI
#include <QDir>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
@ -189,14 +190,16 @@ void OnGameLoaded() {
// We use the QT headers for the xml and json parsing, this define is only true on QT builds
QString patchDir;
Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
QString repositories[] = {"GoldHEN", "shadPS4"};
QDir dir(patchDir);
QStringList folders = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
for (const QString& repository : repositories) {
QString filesJsonPath = patchDir + "/" + repository + "/files.json";
for (const QString& folder : folders) {
QString filesJsonPath = patchDir + "/" + folder + "/files.json";
QFile jsonFile(filesJsonPath);
if (!jsonFile.open(QIODevice::ReadOnly)) {
LOG_ERROR(Loader, "Unable to open files.json for reading.");
LOG_ERROR(Loader, "Unable to open files.json for reading in repository {}",
folder.toStdString());
continue;
}
@ -220,11 +223,12 @@ void OnGameLoaded() {
}
if (selectedFileName.isEmpty()) {
LOG_ERROR(Loader, "No patch file found for the current serial.");
LOG_ERROR(Loader, "No patch file found for the current serial in repository {}",
folder.toStdString());
continue;
}
QString filePath = patchDir + "/" + repository + "/" + selectedFileName;
QString filePath = patchDir + "/" + folder + "/" + selectedFileName;
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
LOG_ERROR(Loader, "Unable to open the file for reading.");

View File

@ -67,28 +67,25 @@ struct AddressSpace::Impl {
static constexpr size_t ReductionOnFail = 1_GB;
static constexpr size_t MaxReductions = 10;
size_t reduction = 0;
size_t virtual_size = SystemManagedSize + SystemReservedSize + UserSize;
for (u32 i = 0; i < MaxReductions; i++) {
virtual_base = static_cast<u8*>(VirtualAlloc2(process, NULL, virtual_size - reduction,
virtual_base = static_cast<u8*>(VirtualAlloc2(process, NULL, virtual_size,
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
PAGE_NOACCESS, &param, 1));
if (virtual_base) {
break;
}
reduction += ReductionOnFail;
virtual_size -= ReductionOnFail;
}
ASSERT_MSG(virtual_base, "Unable to reserve virtual address space: {}",
Common::GetLastErrorMsg());
// Take the reduction off of the system managed area, and leave the others unchanged.
reduction = size_t(virtual_base - SYSTEM_MANAGED_MIN);
system_managed_base = virtual_base;
system_managed_size = SystemManagedSize - reduction;
system_reserved_base = reinterpret_cast<u8*>(SYSTEM_RESERVED_MIN);
system_reserved_size = SystemReservedSize;
system_managed_base = virtual_base;
system_managed_size = system_reserved_base - virtual_base;
user_base = reinterpret_cast<u8*>(USER_MIN);
user_size = UserSize;
user_size = virtual_base + virtual_size - user_base;
LOG_INFO(Kernel_Vmm, "System managed virtual memory region: {} - {}",
fmt::ptr(system_managed_base),
@ -101,10 +98,8 @@ struct AddressSpace::Impl {
// Initializer placeholder tracker
const uintptr_t system_managed_addr = reinterpret_cast<uintptr_t>(system_managed_base);
const uintptr_t system_reserved_addr = reinterpret_cast<uintptr_t>(system_reserved_base);
const uintptr_t user_addr = reinterpret_cast<uintptr_t>(user_base);
regions.emplace(system_managed_addr,
MemoryRegion{system_managed_addr, virtual_size - reduction, false});
MemoryRegion{system_managed_addr, virtual_size, false});
// Allocate backing file that represents the total physical memory.
backing_handle =

View File

@ -30,16 +30,6 @@
using namespace Xbyak::util;
#define MAYBE_AVX(OPCODE, ...) \
[&] { \
Cpu cpu; \
if (cpu.has(Cpu::tAVX)) { \
c.v##OPCODE(__VA_ARGS__); \
} else { \
c.OPCODE(__VA_ARGS__); \
} \
}()
namespace Core {
static Xbyak::Reg ZydisToXbyakRegister(const ZydisRegister reg) {
@ -643,7 +633,7 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera
ASSERT_MSG(length + index <= 64, "length + index must be less than or equal to 64.");
// Get lower qword from xmm register
MAYBE_AVX(movq, scratch1, xmm_dst);
c.vmovq(scratch1, xmm_dst);
if (index != 0) {
c.shr(scratch1, index);
@ -656,7 +646,7 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera
// Writeback to xmm register, extrq instruction says top 64-bits are undefined so we don't
// care to preserve them
MAYBE_AVX(movq, xmm_dst, scratch1);
c.vmovq(xmm_dst, scratch1);
c.pop(scratch2);
c.pop(scratch1);
@ -690,7 +680,7 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera
c.push(mask);
// Construct the mask out of the length that resides in bottom 6 bits of source xmm
MAYBE_AVX(movq, scratch1, xmm_src);
c.vmovq(scratch1, xmm_src);
c.mov(scratch2, scratch1);
c.and_(scratch2, 0x3F);
c.jz(length_zero);
@ -711,10 +701,10 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera
c.and_(scratch1, 0x3F);
c.mov(scratch2, scratch1); // cl now contains the shift amount
MAYBE_AVX(movq, scratch1, xmm_dst);
c.vmovq(scratch1, xmm_dst);
c.shr(scratch1, cl);
c.and_(scratch1, mask);
MAYBE_AVX(movq, xmm_dst, scratch1);
c.vmovq(xmm_dst, scratch1);
c.pop(mask);
c.pop(scratch2);
@ -765,8 +755,8 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
ASSERT_MSG(length + index <= 64, "length + index must be less than or equal to 64.");
MAYBE_AVX(movq, scratch1, xmm_src);
MAYBE_AVX(movq, scratch2, xmm_dst);
c.vmovq(scratch1, xmm_src);
c.vmovq(scratch2, xmm_dst);
c.mov(mask, mask_value);
// src &= mask
@ -784,12 +774,7 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
c.or_(scratch2, scratch1);
// Insert scratch2 into low 64 bits of dst, upper 64 bits are unaffected
Cpu cpu;
if (cpu.has(Cpu::tAVX)) {
c.vpinsrq(xmm_dst, xmm_dst, scratch2, 0);
} else {
c.pinsrq(xmm_dst, scratch2, 0);
}
c.pop(mask);
c.pop(scratch2);
@ -816,7 +801,7 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
c.push(mask);
// Get upper 64 bits of src and copy it to mask and index
MAYBE_AVX(pextrq, index, xmm_src, 1);
c.vpextrq(index, xmm_src, 1);
c.mov(mask, index);
// When length is 0, set it to 64
@ -839,7 +824,7 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
c.and_(index, 0x3F);
// src &= mask
MAYBE_AVX(movq, scratch1, xmm_src);
c.vmovq(scratch1, xmm_src);
c.and_(scratch1, mask);
// mask = ~(mask << index)
@ -851,12 +836,12 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
c.shl(scratch1, cl);
// dst = (dst & mask) | src
MAYBE_AVX(movq, scratch2, xmm_dst);
c.vmovq(scratch2, xmm_dst);
c.and_(scratch2, mask);
c.or_(scratch2, scratch1);
// Upper 64 bits are undefined in insertq
MAYBE_AVX(movq, xmm_dst, scratch2);
c.vmovq(xmm_dst, scratch2);
c.pop(mask);
c.pop(index);

View File

@ -17,6 +17,8 @@ using namespace DebugStateType;
DebugStateImpl& DebugState = *Common::Singleton<DebugStateImpl>::Instance();
bool DebugStateType::showing_debug_menu_bar = false;
static ThreadID ThisThreadID() {
#ifdef _WIN32
return GetCurrentThreadId();

View File

@ -35,6 +35,8 @@ class ShaderList;
namespace DebugStateType {
extern bool showing_debug_menu_bar;
enum class QueueType {
dcb = 0,
ccb = 1,
@ -131,8 +133,6 @@ class DebugStateImpl {
friend class Core::Devtools::Widget::FrameGraph;
friend class Core::Devtools::Widget::ShaderList;
bool showing_debug_menu_bar = false;
std::queue<std::string> debug_message_popup;
std::mutex guest_threads_mutex{};

View File

@ -0,0 +1,74 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "console_device.h"
namespace Core::Devices {
std::shared_ptr<BaseDevice> ConsoleDevice::Create(u32 handle, const char*, int, u16) {
return std::shared_ptr<BaseDevice>(
reinterpret_cast<Devices::BaseDevice*>(new ConsoleDevice(handle)));
}
int ConsoleDevice::ioctl(u64 cmd, Common::VaCtx* args) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 ConsoleDevice::write(const void* buf, size_t nbytes) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
size_t ConsoleDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
size_t ConsoleDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 ConsoleDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 ConsoleDevice::lseek(s64 offset, int whence) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 ConsoleDevice::read(void* buf, size_t nbytes) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
int ConsoleDevice::fstat(Libraries::Kernel::OrbisKernelStat* sb) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s32 ConsoleDevice::fsync() {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
int ConsoleDevice::ftruncate(s64 length) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
int ConsoleDevice::getdents(void* buf, u32 nbytes, s64* basep) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 ConsoleDevice::pwrite(const void* buf, size_t nbytes, u64 offset) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
} // namespace Core::Devices

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "base_device.h"
namespace Core::Devices {
class ConsoleDevice final : BaseDevice {
u32 handle;
public:
static std::shared_ptr<BaseDevice> Create(u32 handle, const char*, int, u16);
explicit ConsoleDevice(u32 handle) : handle(handle) {}
~ConsoleDevice() override = default;
int ioctl(u64 cmd, Common::VaCtx* args) override;
s64 write(const void* buf, size_t nbytes) override;
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override;
s64 lseek(s64 offset, int whence) override;
s64 read(void* buf, size_t nbytes) override;
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override;
s32 fsync() override;
int ftruncate(s64 length) override;
int getdents(void* buf, u32 nbytes, s64* basep) override;
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override;
};
} // namespace Core::Devices

View File

@ -0,0 +1,74 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "deci_tty6_device.h"
namespace Core::Devices {
std::shared_ptr<BaseDevice> DeciTty6Device::Create(u32 handle, const char*, int, u16) {
return std::shared_ptr<BaseDevice>(
reinterpret_cast<Devices::BaseDevice*>(new DeciTty6Device(handle)));
}
int DeciTty6Device::ioctl(u64 cmd, Common::VaCtx* args) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 DeciTty6Device::write(const void* buf, size_t nbytes) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
size_t DeciTty6Device::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
size_t DeciTty6Device::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 DeciTty6Device::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 DeciTty6Device::lseek(s64 offset, int whence) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 DeciTty6Device::read(void* buf, size_t nbytes) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
int DeciTty6Device::fstat(Libraries::Kernel::OrbisKernelStat* sb) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s32 DeciTty6Device::fsync() {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
int DeciTty6Device::ftruncate(s64 length) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
int DeciTty6Device::getdents(void* buf, u32 nbytes, s64* basep) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 DeciTty6Device::pwrite(const void* buf, size_t nbytes, u64 offset) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
} // namespace Core::Devices

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "base_device.h"
namespace Core::Devices {
class DeciTty6Device final : BaseDevice {
u32 handle;
public:
static std::shared_ptr<BaseDevice> Create(u32 handle, const char*, int, u16);
explicit DeciTty6Device(u32 handle) : handle(handle) {}
~DeciTty6Device() override = default;
int ioctl(u64 cmd, Common::VaCtx* args) override;
s64 write(const void* buf, size_t nbytes) override;
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override;
s64 lseek(s64 offset, int whence) override;
s64 read(void* buf, size_t nbytes) override;
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override;
s32 fsync() override;
int ftruncate(s64 length) override;
int getdents(void* buf, u32 nbytes, s64* basep) override;
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override;
};
} // namespace Core::Devices

View File

@ -17,10 +17,12 @@ s64 Logger::write(const void* buf, size_t nbytes) {
}
size_t Logger::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
size_t total_written = 0;
for (int i = 0; i < iovcnt; i++) {
log(static_cast<const char*>(iov[i].iov_base), iov[i].iov_len);
total_written += iov[i].iov_len;
}
return iovcnt;
return total_written;
}
s64 Logger::pwrite(const void* buf, size_t nbytes, u64 offset) {

View File

@ -17,36 +17,47 @@ public:
int ioctl(u64 cmd, Common::VaCtx* args) override {
return 0;
}
s64 write(const void* buf, size_t nbytes) override {
return 0;
}
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override {
return 0;
}
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override {
return 0;
}
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override {
return 0;
}
s64 lseek(s64 offset, int whence) override {
return 0;
}
s64 read(void* buf, size_t nbytes) override {
return 0;
}
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override {
return 0;
}
s32 fsync() override {
return 0;
}
int ftruncate(s64 length) override {
return 0;
}
int getdents(void* buf, u32 nbytes, s64* basep) override {
return 0;
}
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override {
return 0;
}

View File

@ -0,0 +1,78 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdlib>
#include "common/logging/log.h"
#include "random_device.h"
namespace Core::Devices {
std::shared_ptr<BaseDevice> RandomDevice::Create(u32 handle, const char*, int, u16) {
std::srand(std::time(nullptr));
return std::shared_ptr<BaseDevice>(
reinterpret_cast<Devices::BaseDevice*>(new RandomDevice(handle)));
}
int RandomDevice::ioctl(u64 cmd, Common::VaCtx* args) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 RandomDevice::write(const void* buf, size_t nbytes) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
size_t RandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
size_t RandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 RandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 RandomDevice::lseek(s64 offset, int whence) {
return 0;
}
s64 RandomDevice::read(void* buf, size_t nbytes) {
auto rbuf = static_cast<char*>(buf);
for (size_t i = 0; i < nbytes; i++) {
rbuf[i] = std::rand() & 0xFF;
}
return nbytes;
}
int RandomDevice::fstat(Libraries::Kernel::OrbisKernelStat* sb) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s32 RandomDevice::fsync() {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
int RandomDevice::ftruncate(s64 length) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
int RandomDevice::getdents(void* buf, u32 nbytes, s64* basep) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 RandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
} // namespace Core::Devices

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "base_device.h"
namespace Core::Devices {
class RandomDevice final : BaseDevice {
u32 handle;
public:
static std::shared_ptr<BaseDevice> Create(u32 handle, const char*, int, u16);
explicit RandomDevice(u32 handle) : handle(handle) {}
~RandomDevice() override = default;
int ioctl(u64 cmd, Common::VaCtx* args) override;
s64 write(const void* buf, size_t nbytes) override;
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override;
s64 lseek(s64 offset, int whence) override;
s64 read(void* buf, size_t nbytes) override;
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override;
s32 fsync() override;
int ftruncate(s64 length) override;
int getdents(void* buf, u32 nbytes, s64* basep) override;
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override;
};
} // namespace Core::Devices

View File

@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdlib>
#include "common/logging/log.h"
#include "srandom_device.h"
namespace Core::Devices {
std::shared_ptr<BaseDevice> SRandomDevice::Create(u32 handle, const char*, int, u16) {
std::srand(std::time(nullptr));
return std::shared_ptr<BaseDevice>(
reinterpret_cast<Devices::BaseDevice*>(new SRandomDevice(handle)));
}
int SRandomDevice::ioctl(u64 cmd, Common::VaCtx* args) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 SRandomDevice::write(const void* buf, size_t nbytes) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
size_t SRandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
size_t SRandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 SRandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 SRandomDevice::lseek(s64 offset, int whence) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 SRandomDevice::read(void* buf, size_t nbytes) {
auto rbuf = static_cast<char*>(buf);
for (size_t i = 0; i < nbytes; i++) {
rbuf[i] = std::rand();
}
return nbytes;
}
int SRandomDevice::fstat(Libraries::Kernel::OrbisKernelStat* sb) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s32 SRandomDevice::fsync() {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return s32();
}
int SRandomDevice::ftruncate(s64 length) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
int SRandomDevice::getdents(void* buf, u32 nbytes, s64* basep) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 SRandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
} // namespace Core::Devices

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "base_device.h"
namespace Core::Devices {
class SRandomDevice final : BaseDevice {
u32 handle;
public:
static std::shared_ptr<BaseDevice> Create(u32 handle, const char*, int, u16);
explicit SRandomDevice(u32 handle) : handle(handle) {}
~SRandomDevice() override = default;
int ioctl(u64 cmd, Common::VaCtx* args) override;
s64 write(const void* buf, size_t nbytes) override;
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override;
s64 lseek(s64 offset, int whence) override;
s64 read(void* buf, size_t nbytes) override;
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override;
s32 fsync() override;
int ftruncate(s64 length) override;
int getdents(void* buf, u32 nbytes, s64* basep) override;
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override;
};
} // namespace Core::Devices

View File

@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdlib>
#include "common/logging/log.h"
#include "urandom_device.h"
namespace Core::Devices {
std::shared_ptr<BaseDevice> URandomDevice::Create(u32 handle, const char*, int, u16) {
std::srand(std::time(nullptr));
return std::shared_ptr<BaseDevice>(
reinterpret_cast<Devices::BaseDevice*>(new URandomDevice(handle)));
}
int URandomDevice::ioctl(u64 cmd, Common::VaCtx* args) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 URandomDevice::write(const void* buf, size_t nbytes) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
size_t URandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
size_t URandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 URandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 URandomDevice::lseek(s64 offset, int whence) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 URandomDevice::read(void* buf, size_t nbytes) {
auto rbuf = static_cast<char*>(buf);
for (size_t i = 0; i < nbytes; i++) {
rbuf[i] = std::rand() & 0xFF;
}
return nbytes;
}
int URandomDevice::fstat(Libraries::Kernel::OrbisKernelStat* sb) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s32 URandomDevice::fsync() {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
int URandomDevice::ftruncate(s64 length) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
int URandomDevice::getdents(void* buf, u32 nbytes, s64* basep) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
s64 URandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) {
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
return 0;
}
} // namespace Core::Devices

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include "base_device.h"
namespace Core::Devices {
class URandomDevice final : BaseDevice {
u32 handle;
public:
static std::shared_ptr<BaseDevice> Create(u32 handle, const char*, int, u16);
explicit URandomDevice(u32 handle) : handle(handle) {}
~URandomDevice() override = default;
int ioctl(u64 cmd, Common::VaCtx* args) override;
s64 write(const void* buf, size_t nbytes) override;
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override;
s64 lseek(s64 offset, int whence) override;
s64 read(void* buf, size_t nbytes) override;
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override;
s32 fsync() override;
int ftruncate(s64 length) override;
int getdents(void* buf, u32 nbytes, s64* basep) override;
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override;
};
} // namespace Core::Devices

View File

@ -93,6 +93,12 @@ void L::DrawMenuBar() {
}
ImGui::EndMenu();
}
SameLine(ImGui::GetWindowWidth() - 30.0f);
if (Button("X", ImVec2(25, 25))) {
DebugState.IsShowingDebugMenuBar() = false;
}
EndMainMenuBar();
}

View File

@ -24,17 +24,34 @@ using namespace ImGui;
namespace Core::Devtools::Widget {
ShaderList::Selection::Selection(int index) : index(index) {
isa_editor.SetPalette(TextEditor::GetDarkPalette());
isa_editor.SetReadOnly(true);
glsl_editor.SetPalette(TextEditor::GetDarkPalette());
glsl_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL());
ShaderList::Selection::Selection(int index)
: index(index), isa_editor(std::make_unique<TextEditor>()),
glsl_editor(std::make_unique<TextEditor>()) {
isa_editor->SetPalette(TextEditor::GetDarkPalette());
isa_editor->SetReadOnly(true);
glsl_editor->SetPalette(TextEditor::GetDarkPalette());
glsl_editor->SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL());
presenter->GetWindow().RequestKeyboard();
}
ShaderList::Selection::~Selection() {
if (index >= 0) {
presenter->GetWindow().ReleaseKeyboard();
}
}
ShaderList::Selection::Selection(Selection&& other) noexcept
: index{other.index}, isa_editor{std::move(other.isa_editor)},
glsl_editor{std::move(other.glsl_editor)}, open{other.open}, showing_bin{other.showing_bin},
patch_path{std::move(other.patch_path)}, patch_bin_path{std::move(other.patch_bin_path)} {
other.index = -1;
}
ShaderList::Selection& ShaderList::Selection::operator=(Selection other) {
using std::swap;
swap(*this, other);
return *this;
}
void ShaderList::Selection::ReloadShader(DebugStateType::ShaderDump& value) {
auto& spv = value.is_patched ? value.patch_spv : value.spv;
@ -72,13 +89,13 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
value.is_patched = !value.patch_spv.empty();
if (!value.is_patched) { // No patch
isa_editor.SetText(value.cache_isa_disasm);
glsl_editor.SetText(value.cache_spv_disasm);
isa_editor->SetText(value.cache_isa_disasm);
glsl_editor->SetText(value.cache_spv_disasm);
} else {
isa_editor.SetText(value.cache_patch_disasm);
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
glsl_editor.SetText(value.patch_source);
glsl_editor.SetReadOnly(false);
isa_editor->SetText(value.cache_patch_disasm);
isa_editor->SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
glsl_editor->SetText(value.patch_source);
glsl_editor->SetReadOnly(false);
}
}
@ -97,18 +114,18 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
if (value.patch_source.empty()) {
value.patch_source = value.cache_spv_disasm;
}
isa_editor.SetText(value.cache_patch_disasm);
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
glsl_editor.SetText(value.patch_source);
glsl_editor.SetReadOnly(false);
isa_editor->SetText(value.cache_patch_disasm);
isa_editor->SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
glsl_editor->SetText(value.patch_source);
glsl_editor->SetReadOnly(false);
if (!value.patch_spv.empty()) {
ReloadShader(value);
}
} else {
isa_editor.SetText(value.cache_isa_disasm);
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition());
glsl_editor.SetText(value.cache_spv_disasm);
glsl_editor.SetReadOnly(true);
isa_editor->SetText(value.cache_isa_disasm);
isa_editor->SetLanguageDefinition(TextEditor::LanguageDefinition());
glsl_editor->SetText(value.cache_spv_disasm);
glsl_editor->SetReadOnly(true);
ReloadShader(value);
}
}
@ -154,7 +171,7 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
compile = true;
}
if (save) {
value.patch_source = glsl_editor.GetText();
value.patch_source = glsl_editor->GetText();
std::ofstream file{patch_path, std::ios::binary | std::ios::trunc};
file << value.patch_source;
std::string msg = "Patch saved to ";
@ -192,7 +209,7 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
DebugState.ShowDebugMessage("Decompilation failed (Compile was ok):\n" +
res);
} else {
isa_editor.SetText(value.cache_patch_disasm);
isa_editor->SetText(value.cache_patch_disasm);
ReloadShader(value);
}
}
@ -201,9 +218,9 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
}
if (showing_bin) {
isa_editor.Render(value.is_patched ? "SPIRV" : "ISA", GetContentRegionAvail());
isa_editor->Render(value.is_patched ? "SPIRV" : "ISA", GetContentRegionAvail());
} else {
glsl_editor.Render("GLSL", GetContentRegionAvail());
glsl_editor->Render("GLSL", GetContentRegionAvail());
}
End();

View File

@ -14,14 +14,17 @@ class ShaderList {
struct Selection {
explicit Selection(int index);
~Selection();
Selection(const Selection& other) = delete;
Selection(Selection&& other) noexcept;
Selection& operator=(Selection other);
void ReloadShader(DebugStateType::ShaderDump& value);
bool DrawShader(DebugStateType::ShaderDump& value);
int index;
TextEditor isa_editor{};
TextEditor glsl_editor{};
int index{-1};
std::unique_ptr<TextEditor> isa_editor{};
std::unique_ptr<TextEditor> glsl_editor{};
bool open = true;
bool showing_bin = false;

View File

@ -233,7 +233,7 @@ void HandleTable::CreateStdHandles() {
std::shared_ptr<Devices::BaseDevice>{reinterpret_cast<Devices::BaseDevice*>(device)};
};
// order matters
setup("/dev/stdin", new Devices::NopDevice(0)); // stdin
setup("/dev/stdin", new Devices::Logger("stdin", false)); // stdin
setup("/dev/stdout", new Devices::Logger("stdout", false)); // stdout
setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr
}

View File

@ -12,6 +12,7 @@
#include "core/file_sys/fs.h"
#include "core/libraries/app_content/app_content_error.h"
#include "core/libraries/libs.h"
#include "core/libraries/system/systemservice.h"
namespace Libraries::AppContent {
@ -262,6 +263,15 @@ int PS4_SYSV_ABI sceAppContentInitialize(const OrbisAppContentInitParam* initPar
entitlement_label.copy(info.entitlement_label, sizeof(info.entitlement_label));
}
}
if (addcont_count > 0) {
SystemService::OrbisSystemServiceEvent event{};
event.event_type = SystemService::OrbisSystemServiceEventType::EntitlementUpdate;
event.service_entitlement_update.user_id = 0;
event.service_entitlement_update.np_service_label = 0;
SystemService::PushSystemServiceEvent(event);
}
return ORBIS_OK;
}

View File

@ -37,8 +37,9 @@ extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) {
void PS4_SYSV_ABI _sceFiberCheckStackOverflow(OrbisFiberContext* ctx) {
u64* stack_base = reinterpret_cast<u64*>(ctx->current_fiber->addr_context);
u64 stack_size = ctx->current_fiber->size_context;
if (stack_base && *stack_base != kFiberStackSignature) {
UNREACHABLE_MSG("Stack overflow detected in fiber.");
UNREACHABLE_MSG("Stack overflow detected in fiber with size = 0x{:x}", stack_size);
}
}

View File

@ -1366,7 +1366,7 @@ s32 PS4_SYSV_ABI sceGnmSetEmbeddedPsShader(u32* cmdbuf, u32 size, u32 shader_id,
// pointer to a stack memory, so the check will likely fail. To workaround it we will
// repeat set shader functionality here as it is trivial.
cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 8u, ps_regs[0],
0u); // SPI_SHADER_PGM_LO_PS/SPI_SHADER_PGM_HI_PS
ps_regs[1]); // SPI_SHADER_PGM_LO_PS/SPI_SHADER_PGM_HI_PS
cmdbuf = PM4CmdSetData::SetShReg(cmdbuf, 10u, ps_regs[2],
ps_regs[3]); // SPI_SHADER_PGM_RSRC1_PS/SPI_SHADER_PGM_RSRC2_PS
cmdbuf = PM4CmdSetData::SetContextReg(cmdbuf, 0x1c4u, ps_regs[4],

View File

@ -8,8 +8,13 @@
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/singleton.h"
#include "core/devices/console_device.h"
#include "core/devices/deci_tty6_device.h"
#include "core/devices/logger.h"
#include "core/devices/nop_device.h"
#include "core/devices/random_device.h"
#include "core/devices/srandom_device.h"
#include "core/devices/urandom_device.h"
#include "core/file_sys/fs.h"
#include "core/libraries/kernel/file_system.h"
#include "core/libraries/kernel/orbis_error.h"
@ -41,6 +46,12 @@ static std::map<std::string, FactoryDevice> available_device = {
{"/dev/deci_stderr", GET_DEVICE_FD(2)},
{"/dev/null", GET_DEVICE_FD(0)}, // fd0 (stdin) is a nop device
{"/dev/urandom", &D::URandomDevice::Create },
{"/dev/random", &D::RandomDevice::Create },
{"/dev/srandom", &D::SRandomDevice::Create },
{"/dev/console", &D::ConsoleDevice::Create },
{"/dev/deci_tty6",&D::DeciTty6Device::Create }
// clang-format on
};
@ -67,17 +78,6 @@ int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) {
bool directory = (flags & ORBIS_KERNEL_O_DIRECTORY) != 0;
std::string_view path{raw_path};
if (path == "/dev/console") {
return 2000;
}
if (path == "/dev/deci_tty6") {
return 2001;
}
if (path == "/dev/urandom") {
return 2003;
}
u32 handle = h->CreateHandle();
auto* file = h->GetFile(handle);
@ -167,9 +167,6 @@ int PS4_SYSV_ABI sceKernelClose(int d) {
if (d < 3) { // d probably hold an error code
return ORBIS_KERNEL_ERROR_EPERM;
}
if (d == 2003) { // dev/urandom case
return ORBIS_OK;
}
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(d);
if (file == nullptr) {
@ -273,13 +270,6 @@ size_t PS4_SYSV_ABI _readv(int d, const SceKernelIovec* iov, int iovcnt) {
}
size_t PS4_SYSV_ABI _writev(int fd, const SceKernelIovec* iov, int iovcn) {
if (fd == 1) {
size_t total_written = 0;
for (int i = 0; i < iovcn; i++) {
total_written += ::fwrite(iov[i].iov_base, 1, iov[i].iov_len, stdout);
}
return total_written;
}
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(fd);
if (file == nullptr) {
@ -337,13 +327,6 @@ s64 PS4_SYSV_ABI posix_lseek(int d, s64 offset, int whence) {
}
s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes) {
if (d == 2003) // dev urandom case
{
auto rbuf = static_cast<char*>(buf);
for (size_t i = 0; i < nbytes; i++)
rbuf[i] = std::rand() & 0xFF;
return nbytes;
}
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(d);
if (file == nullptr) {
@ -757,7 +740,6 @@ s32 PS4_SYSV_ABI sceKernelRename(const char* from, const char* to) {
}
void RegisterFileSystem(Core::Loader::SymbolsResolver* sym) {
std::srand(std::time(nullptr));
LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, sceKernelOpen);
LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open);
LIB_FUNCTION("wuCroIGjt2g", "libkernel", 1, "libkernel", 1, 1, open);

View File

@ -60,7 +60,7 @@ static void KernelServiceThread(std::stop_token stoken) {
}
io_context.run();
io_context.reset();
io_context.restart();
asio_requests = 0;
}

View File

@ -28,6 +28,7 @@
#include "core/libraries/network/ssl2.h"
#include "core/libraries/np_common/np_common.h"
#include "core/libraries/np_manager/np_manager.h"
#include "core/libraries/np_party/np_party.h"
#include "core/libraries/np_score/np_score.h"
#include "core/libraries/np_trophy/np_trophy.h"
#include "core/libraries/np_web_api/np_web_api.h"
@ -52,6 +53,8 @@
#include "core/libraries/videodec/videodec.h"
#include "core/libraries/videodec/videodec2.h"
#include "core/libraries/videoout/video_out.h"
#include "core/libraries/web_browser_dialog/webbrowserdialog.h"
#include "core/libraries/zlib/zlib_sce.h"
#include "fiber/fiber.h"
#include "jpeg/jpegenc.h"
@ -107,6 +110,9 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::Fiber::RegisterlibSceFiber(sym);
Libraries::JpegEnc::RegisterlibSceJpegEnc(sym);
Libraries::Mouse::RegisterlibSceMouse(sym);
Libraries::WebBrowserDialog::RegisterlibSceWebBrowserDialog(sym);
Libraries::NpParty::RegisterlibSceNpParty(sym);
Libraries::Zlib::RegisterlibSceZlib(sym);
}
} // namespace Libraries

View File

@ -5,6 +5,7 @@
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "core/libraries/np_manager/np_manager.h"
#include "core/libraries/np_manager/np_manager_error.h"
#include "core/tls.h"
namespace Libraries::NpManager {
@ -935,14 +936,22 @@ int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpGetAccountId() {
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id) {
LOG_DEBUG(Lib_NpManager, "called");
if (online_id == nullptr || account_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
*account_id = 0;
return ORBIS_NP_ERROR_SIGNED_OUT;
}
int PS4_SYSV_ABI sceNpGetAccountIdA() {
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
return ORBIS_OK;
int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id) {
LOG_DEBUG(Lib_NpManager, "user_id {}", user_id);
if (account_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
*account_id = 0;
return ORBIS_NP_ERROR_SIGNED_OUT;
}
int PS4_SYSV_ABI sceNpGetAccountLanguage() {
@ -972,6 +981,9 @@ int PS4_SYSV_ABI sceNpGetGamePresenceStatusA() {
int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id) {
LOG_DEBUG(Lib_NpManager, "user_id {}", user_id);
if (np_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
return ORBIS_NP_ERROR_SIGNED_OUT;
}
@ -980,8 +992,11 @@ int PS4_SYSV_ABI sceNpGetNpReachabilityState() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpGetOnlineId(s32 user_id, OrbisNpOnlineId* online_id) {
int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineId* online_id) {
LOG_DEBUG(Lib_NpManager, "user_id {}", user_id);
if (online_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
return ORBIS_NP_ERROR_SIGNED_OUT;
}
@ -995,7 +1010,10 @@ int PS4_SYSV_ABI sceNpGetParentalControlInfoA() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpGetState(s32 userId, OrbisNpState* state) {
int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* state) {
if (state == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
*state = OrbisNpState::SignedOut;
LOG_DEBUG(Lib_NpManager, "Signed out");
return ORBIS_OK;
@ -1011,8 +1029,12 @@ int PS4_SYSV_ABI sceNpGetUserIdByOnlineId() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceNpHasSignedUp() {
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
int PS4_SYSV_ABI sceNpHasSignedUp(OrbisUserServiceUserId user_id, bool* has_signed_up) {
LOG_DEBUG(Lib_NpManager, "called");
if (has_signed_up == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
*has_signed_up = false;
return ORBIS_OK;
}

View File

@ -11,8 +11,6 @@ class SymbolsResolver;
namespace Libraries::NpManager {
constexpr int ORBIS_NP_ERROR_SIGNED_OUT = 0x80550006;
enum class OrbisNpState : u32 { Unknown = 0, SignedOut, SignedIn };
using OrbisNpStateCallbackForNpToolkit = PS4_SYSV_ABI void (*)(s32 userId, OrbisNpState state,
@ -220,22 +218,22 @@ int PS4_SYSV_ABI sceNpGetAccountCountry();
int PS4_SYSV_ABI sceNpGetAccountCountryA();
int PS4_SYSV_ABI sceNpGetAccountDateOfBirth();
int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA();
int PS4_SYSV_ABI sceNpGetAccountId();
int PS4_SYSV_ABI sceNpGetAccountIdA();
int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id);
int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id);
int PS4_SYSV_ABI sceNpGetAccountLanguage();
int PS4_SYSV_ABI sceNpGetAccountLanguage2();
int PS4_SYSV_ABI sceNpGetAccountLanguageA();
int PS4_SYSV_ABI sceNpGetGamePresenceStatus();
int PS4_SYSV_ABI sceNpGetGamePresenceStatusA();
int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId userId, OrbisNpId* npId);
int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id);
int PS4_SYSV_ABI sceNpGetNpReachabilityState();
int PS4_SYSV_ABI sceNpGetOnlineId(s32 userId, OrbisNpOnlineId* onlineId);
int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineId* online_id);
int PS4_SYSV_ABI sceNpGetParentalControlInfo();
int PS4_SYSV_ABI sceNpGetParentalControlInfoA();
int PS4_SYSV_ABI sceNpGetState(s32 userId, OrbisNpState* state);
int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* state);
int PS4_SYSV_ABI sceNpGetUserIdByAccountId();
int PS4_SYSV_ABI sceNpGetUserIdByOnlineId();
int PS4_SYSV_ABI sceNpHasSignedUp();
int PS4_SYSV_ABI sceNpHasSignedUp(OrbisUserServiceUserId user_id, bool* has_signed_up);
int PS4_SYSV_ABI sceNpIdMapperAbortRequest();
int PS4_SYSV_ABI sceNpIdMapperAccountIdToNpId();
int PS4_SYSV_ABI sceNpIdMapperAccountIdToOnlineId();

View File

@ -0,0 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/libraries/error_codes.h"
constexpr int ORBIS_NP_ERROR_INVALID_ARGUMENT = 0x80550003;
constexpr int ORBIS_NP_ERROR_SIGNED_OUT = 0x80550006;

View File

@ -0,0 +1,195 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "core/libraries/np_party/np_party.h"
namespace Libraries::NpParty {
s32 PS4_SYSV_ABI sceNpPartyCheckCallback() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyCreate() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyCreateA() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyGetId() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyGetMemberInfo() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyGetMemberInfoA() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyGetMembers() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyGetMembersA() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyGetMemberSessionInfo() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyGetMemberVoiceInfo() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyGetState() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyGetStateAsUser() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyGetStateAsUserA() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyGetVoiceChatPriority() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyInitialize() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyJoin() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyLeave() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyRegisterHandler() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyRegisterHandlerA() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyRegisterPrivateHandler() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartySendBinaryMessage() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartySetVoiceChatPriority() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyShowInvitationList() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyShowInvitationListA() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyTerminate() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpPartyUnregisterPrivateHandler() {
LOG_ERROR(Lib_NpParty, "(STUBBED) called");
return ORBIS_OK;
}
void RegisterlibSceNpParty(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("3e4k2mzLkmc", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyCheckCallback);
LIB_FUNCTION("nOZRy-slBoA", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyCreate);
LIB_FUNCTION("XQSUbbnpPBA", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyCreateA);
LIB_FUNCTION("DRA3ay-1DFQ", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetId);
LIB_FUNCTION("F1P+-wpxQow", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetMemberInfo);
LIB_FUNCTION("v2RYVGrJDkM", "libSceNpParty", 1, "libSceNpParty", 1, 1,
sceNpPartyGetMemberInfoA);
LIB_FUNCTION("T2UOKf00ZN0", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetMembers);
LIB_FUNCTION("TaNw7W25QJw", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetMembersA);
LIB_FUNCTION("4gOMfNYzllw", "libSceNpParty", 1, "libSceNpParty", 1, 1,
sceNpPartyGetMemberSessionInfo);
LIB_FUNCTION("EKi1jx59SP4", "libSceNpParty", 1, "libSceNpParty", 1, 1,
sceNpPartyGetMemberVoiceInfo);
LIB_FUNCTION("aEzKdJzATZ0", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyGetState);
LIB_FUNCTION("o7grRhiGHYI", "libSceNpParty", 1, "libSceNpParty", 1, 1,
sceNpPartyGetStateAsUser);
LIB_FUNCTION("EjyAI+QNgFw", "libSceNpParty", 1, "libSceNpParty", 1, 1,
sceNpPartyGetStateAsUserA);
LIB_FUNCTION("-lc6XZnQXvM", "libSceNpParty", 1, "libSceNpParty", 1, 1,
sceNpPartyGetVoiceChatPriority);
LIB_FUNCTION("lhYCTQmBkds", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyInitialize);
LIB_FUNCTION("RXNCDw2GDEg", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyJoin);
LIB_FUNCTION("J8jAi-tfJHc", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyLeave);
LIB_FUNCTION("kA88gbv71ao", "libSceNpParty", 1, "libSceNpParty", 1, 1,
sceNpPartyRegisterHandler);
LIB_FUNCTION("+v4fVHMwFWc", "libSceNpParty", 1, "libSceNpParty", 1, 1,
sceNpPartyRegisterHandlerA);
LIB_FUNCTION("zo4G5WWYpKg", "libSceNpParty", 1, "libSceNpParty", 1, 1,
sceNpPartyRegisterPrivateHandler);
LIB_FUNCTION("U6VdUe-PNAY", "libSceNpParty", 1, "libSceNpParty", 1, 1,
sceNpPartySendBinaryMessage);
LIB_FUNCTION("nazKyHygHhY", "libSceNpParty", 1, "libSceNpParty", 1, 1,
sceNpPartySetVoiceChatPriority);
LIB_FUNCTION("-MFiL7hEnPE", "libSceNpParty", 1, "libSceNpParty", 1, 1,
sceNpPartyShowInvitationList);
LIB_FUNCTION("yARHEYLajs0", "libSceNpParty", 1, "libSceNpParty", 1, 1,
sceNpPartyShowInvitationListA);
LIB_FUNCTION("oLYkibiHqRA", "libSceNpParty", 1, "libSceNpParty", 1, 1, sceNpPartyTerminate);
LIB_FUNCTION("zQ7gIvt11Pc", "libSceNpParty", 1, "libSceNpParty", 1, 1,
sceNpPartyUnregisterPrivateHandler);
LIB_FUNCTION("nOZRy-slBoA", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1, sceNpPartyCreate);
LIB_FUNCTION("F1P+-wpxQow", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1,
sceNpPartyGetMemberInfo);
LIB_FUNCTION("T2UOKf00ZN0", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1,
sceNpPartyGetMembers);
LIB_FUNCTION("o7grRhiGHYI", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1,
sceNpPartyGetStateAsUser);
LIB_FUNCTION("kA88gbv71ao", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1,
sceNpPartyRegisterHandler);
LIB_FUNCTION("-MFiL7hEnPE", "libSceNpPartyCompat", 1, "libSceNpParty", 1, 1,
sceNpPartyShowInvitationList);
};
} // namespace Libraries::NpParty

View File

@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::NpParty {
s32 PS4_SYSV_ABI sceNpPartyCheckCallback();
s32 PS4_SYSV_ABI sceNpPartyCreate();
s32 PS4_SYSV_ABI sceNpPartyCreateA();
s32 PS4_SYSV_ABI sceNpPartyGetId();
s32 PS4_SYSV_ABI sceNpPartyGetMemberInfo();
s32 PS4_SYSV_ABI sceNpPartyGetMemberInfoA();
s32 PS4_SYSV_ABI sceNpPartyGetMembers();
s32 PS4_SYSV_ABI sceNpPartyGetMembersA();
s32 PS4_SYSV_ABI sceNpPartyGetMemberSessionInfo();
s32 PS4_SYSV_ABI sceNpPartyGetMemberVoiceInfo();
s32 PS4_SYSV_ABI sceNpPartyGetState();
s32 PS4_SYSV_ABI sceNpPartyGetStateAsUser();
s32 PS4_SYSV_ABI sceNpPartyGetStateAsUserA();
s32 PS4_SYSV_ABI sceNpPartyGetVoiceChatPriority();
s32 PS4_SYSV_ABI sceNpPartyInitialize();
s32 PS4_SYSV_ABI sceNpPartyJoin();
s32 PS4_SYSV_ABI sceNpPartyLeave();
s32 PS4_SYSV_ABI sceNpPartyRegisterHandler();
s32 PS4_SYSV_ABI sceNpPartyRegisterHandlerA();
s32 PS4_SYSV_ABI sceNpPartyRegisterPrivateHandler();
s32 PS4_SYSV_ABI sceNpPartySendBinaryMessage();
s32 PS4_SYSV_ABI sceNpPartySetVoiceChatPriority();
s32 PS4_SYSV_ABI sceNpPartyShowInvitationList();
s32 PS4_SYSV_ABI sceNpPartyShowInvitationListA();
s32 PS4_SYSV_ABI sceNpPartyTerminate();
s32 PS4_SYSV_ABI sceNpPartyUnregisterPrivateHandler();
s32 PS4_SYSV_ABI module_start();
s32 PS4_SYSV_ABI module_stop();
void RegisterlibSceNpParty(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::NpParty

View File

@ -11,6 +11,8 @@
namespace Libraries::Pad {
using Input::GameController;
int PS4_SYSV_ABI scePadClose(s32 handle) {
LOG_ERROR(Lib_Pad, "(STUBBED) called");
return ORBIS_OK;
@ -93,8 +95,8 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn
pInfo->touchPadInfo.pixelDensity = 1;
pInfo->touchPadInfo.resolution.x = 1920;
pInfo->touchPadInfo.resolution.y = 950;
pInfo->stickInfo.deadZoneLeft = 2;
pInfo->stickInfo.deadZoneRight = 2;
pInfo->stickInfo.deadZoneLeft = Config::leftDeadZone();
pInfo->stickInfo.deadZoneRight = Config::rightDeadZone();
pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD;
pInfo->connectedCount = 1;
pInfo->connected = false;
@ -104,8 +106,8 @@ int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerIn
pInfo->touchPadInfo.pixelDensity = 1;
pInfo->touchPadInfo.resolution.x = 1920;
pInfo->touchPadInfo.resolution.y = 950;
pInfo->stickInfo.deadZoneLeft = 2;
pInfo->stickInfo.deadZoneRight = 2;
pInfo->stickInfo.deadZoneLeft = Config::leftDeadZone();
pInfo->stickInfo.deadZoneRight = Config::rightDeadZone();
pInfo->connectionType = ORBIS_PAD_PORT_TYPE_STANDARD;
pInfo->connectedCount = 1;
pInfo->connected = true;
@ -290,7 +292,8 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
int connected_count = 0;
bool connected = false;
Input::State states[64];
auto* controller = Common::Singleton<Input::GameController>::Instance();
auto* controller = Common::Singleton<GameController>::Instance();
const auto* engine = controller->GetEngine();
int ret_num = controller->ReadStates(states, num, &connected, &connected_count);
if (!connected) {
@ -311,9 +314,14 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
pData[i].angularVelocity.x = states[i].angularVelocity.x;
pData[i].angularVelocity.y = states[i].angularVelocity.y;
pData[i].angularVelocity.z = states[i].angularVelocity.z;
Input::GameController::CalculateOrientation(pData[i].acceleration, pData[i].angularVelocity,
1.0f / controller->accel_poll_rate,
pData[i].orientation);
if (engine) {
const auto accel_poll_rate = engine->GetAccelPollRate();
if (accel_poll_rate != 0.0f) {
GameController::CalculateOrientation(pData[i].acceleration,
pData[i].angularVelocity,
1.0f / accel_poll_rate, pData[i].orientation);
}
}
pData[i].touchData.touchNum =
(states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0);
pData[i].touchData.touch[0].x = states[i].touchpad[0].x;
@ -356,7 +364,8 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
if (handle == ORBIS_PAD_ERROR_DEVICE_NO_HANDLE) {
return ORBIS_PAD_ERROR_INVALID_HANDLE;
}
auto* controller = Common::Singleton<Input::GameController>::Instance();
auto* controller = Common::Singleton<GameController>::Instance();
const auto* engine = controller->GetEngine();
int connectedCount = 0;
bool isConnected = false;
Input::State state;
@ -374,9 +383,13 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
pData->angularVelocity.x = state.angularVelocity.x;
pData->angularVelocity.y = state.angularVelocity.y;
pData->angularVelocity.z = state.angularVelocity.z;
Input::GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
1.0f / controller->accel_poll_rate,
pData->orientation);
if (engine) {
const auto accel_poll_rate = engine->GetAccelPollRate();
if (accel_poll_rate != 0.0f) {
GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
1.0f / accel_poll_rate, pData->orientation);
}
}
pData->touchData.touchNum =
(state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0);
pData->touchData.touch[0].x = state.touchpad[0].x;
@ -460,15 +473,15 @@ int PS4_SYSV_ABI scePadSetForceIntercepted() {
int PS4_SYSV_ABI scePadSetLightBar(s32 handle, const OrbisPadLightBarParam* pParam) {
if (pParam != nullptr) {
LOG_INFO(Lib_Pad, "scePadSetLightBar called handle = {} rgb = {} {} {}", handle, pParam->r,
pParam->g, pParam->b);
LOG_DEBUG(Lib_Pad, "called handle = {} rgb = {} {} {}", handle, pParam->r, pParam->g,
pParam->b);
if (pParam->r < 0xD && pParam->g < 0xD && pParam->b < 0xD) {
LOG_INFO(Lib_Pad, "Invalid lightbar setting");
return ORBIS_PAD_ERROR_INVALID_LIGHTBAR_SETTING;
}
auto* controller = Common::Singleton<Input::GameController>::Instance();
auto* controller = Common::Singleton<GameController>::Instance();
controller->SetLightBarRGB(pParam->r, pParam->g, pParam->b);
return ORBIS_OK;
}
@ -536,7 +549,7 @@ int PS4_SYSV_ABI scePadSetVibration(s32 handle, const OrbisPadVibrationParam* pP
if (pParam != nullptr) {
LOG_DEBUG(Lib_Pad, "scePadSetVibration called handle = {} data = {} , {}", handle,
pParam->smallMotor, pParam->largeMotor);
auto* controller = Common::Singleton<Input::GameController>::Instance();
auto* controller = Common::Singleton<GameController>::Instance();
controller->SetVibration(pParam->smallMotor, pParam->largeMotor);
return ORBIS_OK;
}

View File

@ -121,16 +121,18 @@ static void BackupThreadBody() {
std::scoped_lock lk{g_backup_queue_mutex};
g_backup_queue.front().done = true;
}
std::this_thread::sleep_for(std::chrono::seconds(5)); // Don't backup too often
{
std::scoped_lock lk{g_backup_queue_mutex};
g_backup_queue.pop_front();
if (req.origin != OrbisSaveDataEventType::__DO_NOT_SAVE) {
g_result_queue.push_back(std::move(req));
if (g_result_queue.size() > 20) {
g_result_queue.pop_front();
}
}
}
std::this_thread::sleep_for(std::chrono::seconds(5)); // Don't backup too often
}
g_backup_status = WorkerStatus::NotStarted;
}
@ -141,6 +143,15 @@ void StartThread() {
LOG_DEBUG(Lib_SaveData, "Starting backup thread");
g_backup_status = WorkerStatus::Waiting;
g_backup_thread = std::jthread{BackupThreadBody};
static std::once_flag flag;
std::call_once(flag, [] {
std::at_quick_exit([] {
StopThread();
while (GetWorkerStatus() != WorkerStatus::NotStarted) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
});
});
}
void StopThread() {
@ -148,12 +159,12 @@ void StopThread() {
return;
}
LOG_DEBUG(Lib_SaveData, "Stopping backup thread");
g_backup_status = WorkerStatus::Stopping;
{
std::scoped_lock lk{g_backup_queue_mutex};
g_backup_queue.emplace_back(BackupRequest{});
}
g_backup_thread_semaphore.release();
g_backup_status = WorkerStatus::Stopping;
}
bool NewRequest(OrbisUserServiceUserId user_id, std::string_view title_id,

View File

@ -25,6 +25,8 @@ enum class OrbisSaveDataEventType : u32 {
UMOUNT_BACKUP = 1,
BACKUP = 2,
SAVE_DATA_MEMORY_SYNC = 3,
__DO_NOT_SAVE = 1000000, // This value is only for the backup thread
};
struct BackupRequest {

View File

@ -10,6 +10,7 @@
#include "common/path_util.h"
#include "common/singleton.h"
#include "core/file_sys/fs.h"
#include "save_backup.h"
#include "save_instance.h"
constexpr auto OrbisSaveDataBlocksMin2 = 96; // 3MiB
@ -45,17 +46,14 @@ static const std::unordered_map<std::string, std::string> default_title = {
namespace Libraries::SaveData {
std::filesystem::path SaveInstance::MakeTitleSavePath(OrbisUserServiceUserId user_id,
fs::path SaveInstance::MakeTitleSavePath(OrbisUserServiceUserId user_id,
std::string_view game_serial) {
return Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) / std::to_string(user_id) /
game_serial;
return Config::GetSaveDataPath() / std::to_string(user_id) / game_serial;
}
std::filesystem::path SaveInstance::MakeDirSavePath(OrbisUserServiceUserId user_id,
std::string_view game_serial,
fs::path SaveInstance::MakeDirSavePath(OrbisUserServiceUserId user_id, std::string_view game_serial,
std::string_view dir_name) {
return Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) / std::to_string(user_id) /
game_serial / dir_name;
return Config::GetSaveDataPath() / std::to_string(user_id) / game_serial / dir_name;
}
uint64_t SaveInstance::GetMaxBlockFromSFO(const PSF& psf) {
@ -67,7 +65,7 @@ uint64_t SaveInstance::GetMaxBlockFromSFO(const PSF& psf) {
return *(uint64_t*)value.data();
}
std::filesystem::path SaveInstance::GetParamSFOPath(const std::filesystem::path& dir_path) {
fs::path SaveInstance::GetParamSFOPath(const fs::path& dir_path) {
return dir_path / sce_sys / "param.sfo";
}
@ -131,7 +129,6 @@ SaveInstance& SaveInstance::operator=(SaveInstance&& other) noexcept {
save_path = std::move(other.save_path);
param_sfo_path = std::move(other.param_sfo_path);
corrupt_file_path = std::move(other.corrupt_file_path);
corrupt_file = std::move(other.corrupt_file);
param_sfo = std::move(other.param_sfo);
mount_point = std::move(other.mount_point);
max_blocks = other.max_blocks;
@ -144,7 +141,8 @@ SaveInstance& SaveInstance::operator=(SaveInstance&& other) noexcept {
return *this;
}
void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_corrupt) {
void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_corrupt,
bool dont_restore_backup) {
if (mounted) {
UNREACHABLE_MSG("Save instance is already mounted");
}
@ -163,25 +161,27 @@ void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_cor
}
exists = true;
} else {
std::optional<fs::filesystem_error> err;
if (!ignore_corrupt && fs::exists(corrupt_file_path)) {
throw std::filesystem::filesystem_error(
"Corrupted save data", corrupt_file_path,
err = fs::filesystem_error("Corrupted save data", corrupt_file_path,
std::make_error_code(std::errc::illegal_byte_sequence));
} else if (!param_sfo.Open(param_sfo_path)) {
err = fs::filesystem_error("Failed to read param.sfo", param_sfo_path,
std::make_error_code(std::errc::illegal_byte_sequence));
}
if (!param_sfo.Open(param_sfo_path)) {
throw std::filesystem::filesystem_error(
"Failed to read param.sfo", param_sfo_path,
std::make_error_code(std::errc::illegal_byte_sequence));
if (err.has_value()) {
if (dont_restore_backup) {
throw err.value();
}
if (Backup::Restore(save_path)) {
return SetupAndMount(read_only, copy_icon, ignore_corrupt, true);
}
}
}
if (!ignore_corrupt && !read_only) {
int err = corrupt_file.Open(corrupt_file_path, Common::FS::FileAccessMode::Write);
if (err != 0) {
throw std::filesystem::filesystem_error(
"Failed to open corrupted file", corrupt_file_path,
std::make_error_code(std::errc::illegal_byte_sequence));
}
Common::FS::IOFile f(corrupt_file_path, Common::FS::FileAccessMode::Write);
f.Close();
}
max_blocks = static_cast<int>(GetMaxBlockFromSFO(param_sfo));
@ -199,12 +199,11 @@ void SaveInstance::Umount() {
mounted = false;
const bool ok = param_sfo.Encode(param_sfo_path);
if (!ok) {
throw std::filesystem::filesystem_error("Failed to write param.sfo", param_sfo_path,
throw fs::filesystem_error("Failed to write param.sfo", param_sfo_path,
std::make_error_code(std::errc::permission_denied));
}
param_sfo = PSF();
corrupt_file.Close();
fs::remove(corrupt_file_path);
g_mnt->Unmount(save_path, mount_point);
}
@ -218,7 +217,7 @@ void SaveInstance::CreateFiles() {
const bool ok = param_sfo.Encode(param_sfo_path);
if (!ok) {
throw std::filesystem::filesystem_error("Failed to write param.sfo", param_sfo_path,
throw fs::filesystem_error("Failed to write param.sfo", param_sfo_path,
std::make_error_code(std::errc::permission_denied));
}
}

View File

@ -42,8 +42,6 @@ class SaveInstance {
std::filesystem::path param_sfo_path;
std::filesystem::path corrupt_file_path;
Common::FS::IOFile corrupt_file;
PSF param_sfo;
std::string mount_point;
@ -80,7 +78,8 @@ public:
SaveInstance& operator=(const SaveInstance& other) = delete;
SaveInstance& operator=(SaveInstance&& other) noexcept;
void SetupAndMount(bool read_only = false, bool copy_icon = false, bool ignore_corrupt = false);
void SetupAndMount(bool read_only = false, bool copy_icon = false, bool ignore_corrupt = false,
bool dont_restore_backup = false);
void Umount();

View File

@ -6,14 +6,16 @@
#include <condition_variable>
#include <filesystem>
#include <mutex>
#include <thread>
#include <utility>
#include <fmt/format.h>
#include <core/libraries/system/msgdialog_ui.h>
#include "common/assert.h"
#include "common/elf_info.h"
#include "common/logging/log.h"
#include "common/polyfill_thread.h"
#include "common/path_util.h"
#include "common/singleton.h"
#include "common/thread.h"
#include "core/file_sys/fs.h"
@ -23,265 +25,202 @@ using Common::FS::IOFile;
namespace fs = std::filesystem;
constexpr std::string_view sce_sys = "sce_sys"; // system folder inside save
constexpr std::string_view DirnameSaveDataMemory = "sce_sdmemory";
constexpr std::string_view StandardDirnameSaveDataMemory = "sce_sdmemory";
constexpr std::string_view FilenameSaveDataMemory = "memory.dat";
constexpr std::string_view IconName = "icon0.png";
constexpr std::string_view CorruptFileName = "corrupted";
namespace Libraries::SaveData::SaveMemory {
static Core::FileSys::MntPoints* g_mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
static OrbisUserServiceUserId g_user_id{};
static std::string g_game_serial{};
static std::filesystem::path g_save_path{};
static std::filesystem::path g_param_sfo_path{};
static PSF g_param_sfo;
struct SlotData {
OrbisUserServiceUserId user_id;
std::string game_serial;
std::filesystem::path folder_path;
PSF sfo;
std::vector<u8> memory_cache;
};
static bool g_save_memory_initialized = false;
static std::mutex g_saving_memory_mutex;
static std::vector<u8> g_save_memory;
static std::mutex g_slot_mtx;
static std::unordered_map<u32, SlotData> g_attached_slots;
static std::filesystem::path g_icon_path;
static std::vector<u8> g_icon_memory;
static std::condition_variable g_trigger_save_memory;
static std::atomic_bool g_saving_memory = false;
static std::atomic_bool g_save_event = false;
static std::jthread g_save_memory_thread;
static std::atomic_bool g_memory_dirty = false;
static std::atomic_bool g_param_dirty = false;
static std::atomic_bool g_icon_dirty = false;
static void SaveFileSafe(void* buf, size_t count, const std::filesystem::path& path) {
const auto& dir = path.parent_path();
const auto& name = path.filename();
const auto tmp_path = dir / (name.string() + ".tmp");
IOFile file(tmp_path, Common::FS::FileAccessMode::Write);
file.WriteRaw<u8>(buf, count);
file.Close();
fs::remove(path);
fs::rename(tmp_path, path);
void PersistMemory(u32 slot_id, bool lock) {
std::unique_lock lck{g_slot_mtx, std::defer_lock};
if (lock) {
lck.lock();
}
auto& data = g_attached_slots[slot_id];
auto memoryPath = data.folder_path / FilenameSaveDataMemory;
fs::create_directories(memoryPath.parent_path());
[[noreturn]] void SaveThreadLoop() {
Common::SetCurrentThreadName("shadPS4:SaveData:SaveDataMemoryThread");
std::mutex mtx;
while (true) {
{
std::unique_lock lk{mtx};
g_trigger_save_memory.wait(lk);
}
// Save the memory
g_saving_memory = true;
std::scoped_lock lk{g_saving_memory_mutex};
int n = 0;
std::string errMsg;
while (n++ < 10) {
try {
LOG_DEBUG(Lib_SaveData, "Saving save data memory {}", fmt::UTF(g_save_path.u8string()));
if (g_memory_dirty) {
g_memory_dirty = false;
SaveFileSafe(g_save_memory.data(), g_save_memory.size(),
g_save_path / FilenameSaveDataMemory);
}
if (g_param_dirty) {
g_param_dirty = false;
static std::vector<u8> buf;
g_param_sfo.Encode(buf);
SaveFileSafe(buf.data(), buf.size(), g_param_sfo_path);
}
if (g_icon_dirty) {
g_icon_dirty = false;
SaveFileSafe(g_icon_memory.data(), g_icon_memory.size(), g_icon_path);
}
if (g_save_event) {
Backup::PushBackupEvent(Backup::BackupRequest{
.user_id = g_user_id,
.title_id = g_game_serial,
.dir_name = std::string{DirnameSaveDataMemory},
.origin = Backup::OrbisSaveDataEventType::SAVE_DATA_MEMORY_SYNC,
.save_path = g_save_path,
});
g_save_event = false;
}
} catch (const fs::filesystem_error& e) {
LOG_ERROR(Lib_SaveData, "Failed to save save data memory: {}", e.what());
MsgDialog::ShowMsgDialog(MsgDialog::MsgDialogState{
MsgDialog::MsgDialogState::UserState{
.type = MsgDialog::ButtonType::OK,
.msg = fmt::format("Failed to save save data memory.\nCode: <{}>\n{}",
e.code().message(), e.what()),
},
});
}
g_saving_memory = false;
}
}
void SetDirectories(OrbisUserServiceUserId user_id, std::string _game_serial) {
g_user_id = user_id;
g_game_serial = std::move(_game_serial);
g_save_path = SaveInstance::MakeDirSavePath(user_id, g_game_serial, DirnameSaveDataMemory);
g_param_sfo_path = SaveInstance::GetParamSFOPath(g_save_path);
g_param_sfo = PSF();
g_icon_path = g_save_path / sce_sys / "icon0.png";
}
const std::filesystem::path& GetSavePath() {
return g_save_path;
}
size_t CreateSaveMemory(size_t memory_size) {
size_t existed_size = 0;
static std::once_flag init_save_thread_flag;
std::call_once(init_save_thread_flag,
[] { g_save_memory_thread = std::jthread{SaveThreadLoop}; });
g_save_memory.resize(memory_size);
SaveInstance::SetupDefaultParamSFO(g_param_sfo, std::string{DirnameSaveDataMemory},
g_game_serial);
g_save_memory_initialized = true;
if (!fs::exists(g_param_sfo_path)) {
// Create save memory
fs::create_directories(g_save_path / sce_sys);
IOFile memory_file{g_save_path / FilenameSaveDataMemory, Common::FS::FileAccessMode::Write};
bool ok = memory_file.SetSize(memory_size);
if (!ok) {
LOG_ERROR(Lib_SaveData, "Failed to set memory size");
throw std::filesystem::filesystem_error(
"Failed to set save memory size", g_save_path / FilenameSaveDataMemory,
std::make_error_code(std::errc::no_space_on_device));
}
memory_file.Close();
} else {
// Load save memory
bool ok = g_param_sfo.Open(g_param_sfo_path);
if (!ok) {
LOG_ERROR(Lib_SaveData, "Failed to open SFO at {}",
fmt::UTF(g_param_sfo_path.u8string()));
throw std::filesystem::filesystem_error(
"failed to open SFO", g_param_sfo_path,
std::make_error_code(std::errc::illegal_byte_sequence));
}
IOFile memory_file{g_save_path / FilenameSaveDataMemory, Common::FS::FileAccessMode::Read};
if (!memory_file.IsOpen()) {
LOG_ERROR(Lib_SaveData, "Failed to open save memory");
throw std::filesystem::filesystem_error(
"failed to open save memory", g_save_path / FilenameSaveDataMemory,
std::make_error_code(std::errc::permission_denied));
}
size_t save_size = memory_file.GetSize();
existed_size = save_size;
memory_file.Seek(0);
memory_file.ReadRaw<u8>(g_save_memory.data(), std::min(save_size, memory_size));
memory_file.Close();
}
return existed_size;
}
void SetIcon(void* buf, size_t buf_size) {
if (buf == nullptr) {
const auto& src_icon = g_mnt->GetHostPath("/app0/sce_sys/save_data.png");
if (fs::exists(src_icon)) {
if (fs::exists(g_icon_path)) {
fs::remove(g_icon_path);
}
fs::copy_file(src_icon, g_icon_path);
}
if (fs::exists(g_icon_path)) {
IOFile file(g_icon_path, Common::FS::FileAccessMode::Read);
size_t size = file.GetSize();
file.Seek(0);
g_icon_memory.resize(size);
file.ReadRaw<u8>(g_icon_memory.data(), size);
file.Close();
}
} else {
g_icon_memory.resize(buf_size);
std::memcpy(g_icon_memory.data(), buf, buf_size);
IOFile file(g_icon_path, Common::FS::FileAccessMode::Write);
file.Seek(0);
file.WriteRaw<u8>(g_icon_memory.data(), buf_size);
file.Close();
}
}
void WriteIcon(void* buf, size_t buf_size) {
if (buf_size != g_icon_memory.size()) {
g_icon_memory.resize(buf_size);
}
std::memcpy(g_icon_memory.data(), buf, buf_size);
g_icon_dirty = true;
}
bool IsSaveMemoryInitialized() {
return g_save_memory_initialized;
}
PSF& GetParamSFO() {
return g_param_sfo;
}
std::span<u8> GetIcon() {
return {g_icon_memory};
}
void SaveSFO(bool sync) {
if (!sync) {
g_param_dirty = true;
IOFile f;
int r = f.Open(memoryPath, Common::FS::FileAccessMode::Write);
if (f.IsOpen()) {
f.WriteRaw<u8>(data.memory_cache.data(), data.memory_cache.size());
f.Close();
return;
}
const bool ok = g_param_sfo.Encode(g_param_sfo_path);
const auto err = std::error_code{r, std::iostream_category()};
throw std::filesystem::filesystem_error{err.message(), err};
} catch (const std::filesystem::filesystem_error& e) {
errMsg = std::string{e.what()};
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
const MsgDialog::MsgDialogState dialog{MsgDialog::MsgDialogState::UserState{
.type = MsgDialog::ButtonType::OK,
.msg = "Failed to persist save memory:\n" + errMsg + "\nat " +
Common::FS::PathToUTF8String(memoryPath),
}};
MsgDialog::ShowMsgDialog(dialog);
}
std::string GetSaveDir(u32 slot_id) {
std::string dir(StandardDirnameSaveDataMemory);
if (slot_id > 0) {
dir += std::to_string(slot_id);
}
return dir;
}
std::filesystem::path GetSavePath(OrbisUserServiceUserId user_id, u32 slot_id,
std::string_view game_serial) {
std::string dir(StandardDirnameSaveDataMemory);
if (slot_id > 0) {
dir += std::to_string(slot_id);
}
return SaveInstance::MakeDirSavePath(user_id, Common::ElfInfo::Instance().GameSerial(), dir);
}
size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_view game_serial) {
std::lock_guard lck{g_slot_mtx};
const auto save_dir = GetSavePath(user_id, slot_id, game_serial);
auto& data = g_attached_slots[slot_id];
data = SlotData{
.user_id = user_id,
.game_serial = std::string{game_serial},
.folder_path = save_dir,
.sfo = {},
.memory_cache = {},
};
SaveInstance::SetupDefaultParamSFO(data.sfo, GetSaveDir(slot_id), std::string{game_serial});
auto param_sfo_path = SaveInstance::GetParamSFOPath(save_dir);
if (!fs::exists(param_sfo_path)) {
return 0;
}
if (!data.sfo.Open(param_sfo_path) || fs::exists(save_dir / CorruptFileName)) {
if (!Backup::Restore(save_dir)) { // Could not restore the backup
return 0;
}
}
const auto memory = save_dir / FilenameSaveDataMemory;
if (fs::exists(memory)) {
return fs::file_size(memory);
}
return 0;
}
void SetIcon(u32 slot_id, void* buf, size_t buf_size) {
std::lock_guard lck{g_slot_mtx};
const auto& data = g_attached_slots[slot_id];
const auto icon_path = data.folder_path / sce_sys / "icon0.png";
if (buf == nullptr) {
const auto& src_icon = g_mnt->GetHostPath("/app0/sce_sys/save_data.png");
if (fs::exists(icon_path)) {
fs::remove(icon_path);
}
if (fs::exists(src_icon)) {
fs::create_directories(icon_path.parent_path());
fs::copy_file(src_icon, icon_path);
}
} else {
IOFile file(icon_path, Common::FS::FileAccessMode::Write);
file.WriteRaw<u8>(buf, buf_size);
file.Close();
}
}
bool IsSaveMemoryInitialized(u32 slot_id) {
std::lock_guard lck{g_slot_mtx};
return g_attached_slots.contains(slot_id);
}
PSF& GetParamSFO(u32 slot_id) {
std::lock_guard lck{g_slot_mtx};
auto& data = g_attached_slots[slot_id];
return data.sfo;
}
std::vector<u8> GetIcon(u32 slot_id) {
std::lock_guard lck{g_slot_mtx};
auto& data = g_attached_slots[slot_id];
const auto icon_path = data.folder_path / sce_sys / "icon0.png";
IOFile f{icon_path, Common::FS::FileAccessMode::Read};
if (!f.IsOpen()) {
return {};
}
const u64 size = f.GetSize();
std::vector<u8> ret;
ret.resize(size);
f.ReadSpan(std::span{ret});
return ret;
}
void SaveSFO(u32 slot_id) {
std::lock_guard lck{g_slot_mtx};
const auto& data = g_attached_slots[slot_id];
const auto sfo_path = SaveInstance::GetParamSFOPath(data.folder_path);
fs::create_directories(sfo_path.parent_path());
const bool ok = data.sfo.Encode(sfo_path);
if (!ok) {
LOG_ERROR(Lib_SaveData, "Failed to encode param.sfo");
throw std::filesystem::filesystem_error("Failed to write param.sfo", g_param_sfo_path,
throw std::filesystem::filesystem_error("Failed to write param.sfo", sfo_path,
std::make_error_code(std::errc::permission_denied));
}
}
bool IsSaving() {
return g_saving_memory;
void ReadMemory(u32 slot_id, void* buf, size_t buf_size, int64_t offset) {
std::lock_guard lk{g_slot_mtx};
auto& data = g_attached_slots[slot_id];
auto& memory = data.memory_cache;
if (memory.empty()) { // Load file
IOFile f{data.folder_path / FilenameSaveDataMemory, Common::FS::FileAccessMode::Read};
if (f.IsOpen()) {
memory.resize(f.GetSize());
f.Seek(0);
f.ReadSpan(std::span{memory});
}
}
s64 read_size = buf_size;
if (read_size + offset > memory.size()) {
read_size = memory.size() - offset;
}
std::memcpy(buf, memory.data() + offset, read_size);
}
bool TriggerSaveWithoutEvent() {
if (g_saving_memory) {
return false;
void WriteMemory(u32 slot_id, void* buf, size_t buf_size, int64_t offset) {
std::lock_guard lk{g_slot_mtx};
auto& data = g_attached_slots[slot_id];
auto& memory = data.memory_cache;
if (offset + buf_size > memory.size()) {
memory.resize(offset + buf_size);
}
g_trigger_save_memory.notify_one();
return true;
}
bool TriggerSave() {
if (g_saving_memory) {
return false;
}
g_save_event = true;
g_trigger_save_memory.notify_one();
return true;
}
void ReadMemory(void* buf, size_t buf_size, int64_t offset) {
std::scoped_lock lk{g_saving_memory_mutex};
if (offset + buf_size > g_save_memory.size()) {
UNREACHABLE_MSG("ReadMemory out of bounds");
}
std::memcpy(buf, g_save_memory.data() + offset, buf_size);
}
void WriteMemory(void* buf, size_t buf_size, int64_t offset) {
std::scoped_lock lk{g_saving_memory_mutex};
if (offset + buf_size > g_save_memory.size()) {
g_save_memory.resize(offset + buf_size);
}
std::memcpy(g_save_memory.data() + offset, buf, buf_size);
g_memory_dirty = true;
std::memcpy(memory.data() + offset, buf, buf_size);
PersistMemory(slot_id, false);
Backup::NewRequest(data.user_id, data.game_serial, GetSaveDir(slot_id),
Backup::OrbisSaveDataEventType::__DO_NOT_SAVE);
}
} // namespace Libraries::SaveData::SaveMemory

View File

@ -3,7 +3,7 @@
#pragma once
#include <span>
#include <vector>
#include "save_backup.h"
class PSF;
@ -14,36 +14,30 @@ using OrbisUserServiceUserId = s32;
namespace Libraries::SaveData::SaveMemory {
void SetDirectories(OrbisUserServiceUserId user_id, std::string game_serial);
void PersistMemory(u32 slot_id, bool lock = true);
[[nodiscard]] const std::filesystem::path& GetSavePath();
[[nodiscard]] std::string GetSaveDir(u32 slot_id);
// returns the size of the existed save memory
size_t CreateSaveMemory(size_t memory_size);
[[nodiscard]] std::filesystem::path GetSavePath(OrbisUserServiceUserId user_id, u32 slot_id,
std::string_view game_serial);
// Initialize the icon. Set buf to null to read the standard icon.
void SetIcon(void* buf, size_t buf_size);
// returns the size of the save memory if exists
size_t SetupSaveMemory(OrbisUserServiceUserId user_id, u32 slot_id, std::string_view game_serial);
// Update the icon
void WriteIcon(void* buf, size_t buf_size);
// Write the icon. Set buf to null to read the standard icon.
void SetIcon(u32 slot_id, void* buf = nullptr, size_t buf_size = 0);
[[nodiscard]] bool IsSaveMemoryInitialized();
[[nodiscard]] bool IsSaveMemoryInitialized(u32 slot_id);
[[nodiscard]] PSF& GetParamSFO();
[[nodiscard]] PSF& GetParamSFO(u32 slot_id);
[[nodiscard]] std::span<u8> GetIcon();
[[nodiscard]] std::vector<u8> GetIcon(u32 slot_id);
// Save now or wait for the background thread to save
void SaveSFO(bool sync = false);
void SaveSFO(u32 slot_id);
[[nodiscard]] bool IsSaving();
void ReadMemory(u32 slot_id, void* buf, size_t buf_size, int64_t offset);
bool TriggerSaveWithoutEvent();
bool TriggerSave();
void ReadMemory(void* buf, size_t buf_size, int64_t offset);
void WriteMemory(void* buf, size_t buf_size, int64_t offset);
void WriteMemory(u32 slot_id, void* buf, size_t buf_size, int64_t offset);
} // namespace Libraries::SaveData::SaveMemory

View File

@ -177,7 +177,8 @@ struct OrbisSaveDataMemoryGet2 {
OrbisSaveDataMemoryData* data;
OrbisSaveDataParam* param;
OrbisSaveDataIcon* icon;
std::array<u8, 32> _reserved;
u32 slotId;
std::array<u8, 28> _reserved;
};
struct OrbisSaveDataMemorySet2 {
@ -186,6 +187,8 @@ struct OrbisSaveDataMemorySet2 {
const OrbisSaveDataMemoryData* data;
const OrbisSaveDataParam* param;
const OrbisSaveDataIcon* icon;
u32 dataNum;
u32 slotId;
std::array<u8, 32> _reserved;
};
@ -198,7 +201,8 @@ struct OrbisSaveDataMemorySetup2 {
const OrbisSaveDataParam* initParam;
// +4.5
const OrbisSaveDataIcon* initIcon;
std::array<u8, 24> _reserved;
u32 slotId;
std::array<u8, 20> _reserved;
};
struct OrbisSaveDataMemorySetupResult {
@ -206,9 +210,16 @@ struct OrbisSaveDataMemorySetupResult {
std::array<u8, 16> _reserved;
};
enum OrbisSaveDataMemorySyncOption : u32 {
NONE = 0,
BLOCKING = 1,
};
struct OrbisSaveDataMemorySync {
OrbisUserServiceUserId userId;
std::array<u8, 36> _reserved;
u32 slotId;
OrbisSaveDataMemorySyncOption option;
std::array<u8, 28> _reserved;
};
struct OrbisSaveDataMount2 {
@ -327,6 +338,7 @@ static void initialize() {
g_initialized = true;
g_game_serial = ElfInfo::Instance().GameSerial();
g_fw_ver = ElfInfo::Instance().FirmwareVer();
Backup::StartThread();
}
// game_00other | game*other
@ -558,7 +570,6 @@ Error PS4_SYSV_ABI sceSaveDataBackup(const OrbisSaveDataBackup* backup) {
}
}
Backup::StartThread();
Backup::NewRequest(backup->userId, title, dir_name, OrbisSaveDataEventType::BACKUP);
return Error::OK;
@ -1136,22 +1147,27 @@ Error PS4_SYSV_ABI sceSaveDataGetSaveDataMemory2(OrbisSaveDataMemoryGet2* getPar
LOG_INFO(Lib_SaveData, "called with invalid parameter");
return Error::PARAMETER;
}
if (!SaveMemory::IsSaveMemoryInitialized()) {
u32 slot_id = 0;
if (g_fw_ver > ElfInfo::FW_50) {
slot_id = getParam->slotId;
}
if (!SaveMemory::IsSaveMemoryInitialized(slot_id)) {
LOG_INFO(Lib_SaveData, "called without save memory initialized");
return Error::MEMORY_NOT_READY;
}
LOG_DEBUG(Lib_SaveData, "called");
auto data = getParam->data;
if (data != nullptr) {
SaveMemory::ReadMemory(data->buf, data->bufSize, data->offset);
SaveMemory::ReadMemory(slot_id, data->buf, data->bufSize, data->offset);
}
auto param = getParam->param;
if (param != nullptr) {
param->FromSFO(SaveMemory::GetParamSFO());
param->FromSFO(SaveMemory::GetParamSFO(slot_id));
}
auto icon = getParam->icon;
if (icon != nullptr) {
auto icon_mem = SaveMemory::GetIcon();
auto icon_mem = SaveMemory::GetIcon(slot_id);
size_t total = std::min(icon->bufSize, icon_mem.size());
std::memcpy(icon->buf, icon_mem.data(), total);
icon->dataSize = total;
@ -1494,36 +1510,37 @@ Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2*
LOG_INFO(Lib_SaveData, "called with invalid parameter");
return Error::PARAMETER;
}
if (!SaveMemory::IsSaveMemoryInitialized()) {
u32 slot_id = 0;
u32 data_num = 1;
if (g_fw_ver > ElfInfo::FW_50) {
slot_id = setParam->slotId;
if (setParam->dataNum > 1) {
data_num = setParam->dataNum;
}
}
if (!SaveMemory::IsSaveMemoryInitialized(slot_id)) {
LOG_INFO(Lib_SaveData, "called without save memory initialized");
return Error::MEMORY_NOT_READY;
}
if (SaveMemory::IsSaving()) {
int count = 0;
while (++count < 100 && SaveMemory::IsSaving()) { // try for more 10 seconds
std::this_thread::sleep_for(chrono::milliseconds(100));
}
if (SaveMemory::IsSaving()) {
LOG_TRACE(Lib_SaveData, "called while saving");
return Error::BUSY_FOR_SAVING;
}
}
LOG_DEBUG(Lib_SaveData, "called");
auto data = setParam->data;
if (data != nullptr) {
SaveMemory::WriteMemory(data->buf, data->bufSize, data->offset);
for (int i = 0; i < data_num; i++) {
SaveMemory::WriteMemory(slot_id, data[i].buf, data[i].bufSize, data[i].offset);
}
}
auto param = setParam->param;
if (param != nullptr) {
param->ToSFO(SaveMemory::GetParamSFO());
SaveMemory::SaveSFO();
}
auto icon = setParam->icon;
if (icon != nullptr) {
SaveMemory::WriteIcon(icon->buf, icon->bufSize);
param->ToSFO(SaveMemory::GetParamSFO(slot_id));
SaveMemory::SaveSFO(slot_id);
}
auto icon = setParam->icon;
if (icon != nullptr) {
SaveMemory::SetIcon(slot_id, icon->buf, icon->bufSize);
}
SaveMemory::TriggerSaveWithoutEvent();
return Error::OK;
}
@ -1563,9 +1580,12 @@ Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetu
}
LOG_DEBUG(Lib_SaveData, "called");
SaveMemory::SetDirectories(setupParam->userId, g_game_serial);
u32 slot_id = 0;
if (g_fw_ver > ElfInfo::FW_50) {
slot_id = setupParam->slotId;
}
const auto& save_path = SaveMemory::GetSavePath();
const auto& save_path = SaveMemory::GetSavePath(setupParam->userId, slot_id, g_game_serial);
for (const auto& instance : g_mount_slots) {
if (instance.has_value() && instance->GetSavePath() == save_path) {
return Error::BUSY;
@ -1573,21 +1593,21 @@ Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetu
}
try {
size_t existed_size = SaveMemory::CreateSaveMemory(setupParam->memorySize);
size_t existed_size =
SaveMemory::SetupSaveMemory(setupParam->userId, slot_id, g_game_serial);
if (existed_size == 0) { // Just created
if (g_fw_ver >= ElfInfo::FW_45 && setupParam->initParam != nullptr) {
auto& sfo = SaveMemory::GetParamSFO();
auto& sfo = SaveMemory::GetParamSFO(slot_id);
setupParam->initParam->ToSFO(sfo);
}
SaveMemory::SaveSFO();
SaveMemory::SaveSFO(slot_id);
auto init_icon = setupParam->initIcon;
if (g_fw_ver >= ElfInfo::FW_45 && init_icon != nullptr) {
SaveMemory::SetIcon(init_icon->buf, init_icon->bufSize);
SaveMemory::SetIcon(slot_id, init_icon->buf, init_icon->bufSize);
} else {
SaveMemory::SetIcon(nullptr, 0);
SaveMemory::SetIcon(slot_id);
}
SaveMemory::TriggerSaveWithoutEvent();
}
if (g_fw_ver >= ElfInfo::FW_45 && result != nullptr) {
result->existedMemorySize = existed_size;
@ -1631,15 +1651,23 @@ Error PS4_SYSV_ABI sceSaveDataSyncSaveDataMemory(OrbisSaveDataMemorySync* syncPa
LOG_INFO(Lib_SaveData, "called with invalid parameter");
return Error::PARAMETER;
}
if (!SaveMemory::IsSaveMemoryInitialized()) {
u32 slot_id = 0;
if (g_fw_ver > ElfInfo::FW_50) {
slot_id = syncParam->slotId;
}
if (!SaveMemory::IsSaveMemoryInitialized(slot_id)) {
LOG_INFO(Lib_SaveData, "called without save memory initialized");
return Error::MEMORY_NOT_READY;
}
LOG_DEBUG(Lib_SaveData, "called");
bool ok = SaveMemory::TriggerSave();
if (!ok) {
return Error::BUSY_FOR_SAVING;
}
SaveMemory::PersistMemory(slot_id);
const auto& save_path = SaveMemory::GetSaveDir(slot_id);
Backup::NewRequest(syncParam->userId, g_game_serial, save_path,
OrbisSaveDataEventType::SAVE_DATA_MEMORY_SYNC);
return Error::OK;
}

View File

@ -10,6 +10,8 @@
namespace Libraries::SystemService {
bool g_splash_status{true};
std::queue<OrbisSystemServiceEvent> g_event_queue;
std::mutex g_event_queue_mutex;
bool IsSplashVisible() {
return Config::showSplash() && g_splash_status;
@ -1772,7 +1774,9 @@ s32 PS4_SYSV_ABI sceSystemServiceGetStatus(OrbisSystemServiceStatus* status) {
LOG_ERROR(Lib_SystemService, "OrbisSystemServiceStatus is null");
return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER;
}
status->event_num = 0;
std::lock_guard<std::mutex> lock(g_event_queue_mutex);
status->event_num = static_cast<s32>(g_event_queue.size());
status->is_system_ui_overlaid = false;
status->is_in_background_execution = false;
status->is_cpu_mode7_cpu_normal = true;
@ -1940,13 +1944,21 @@ int PS4_SYSV_ABI sceSystemServiceRaiseExceptionLocalProcess() {
}
s32 PS4_SYSV_ABI sceSystemServiceReceiveEvent(OrbisSystemServiceEvent* event) {
LOG_ERROR(Lib_SystemService, "(STUBBED) called");
LOG_TRACE(Lib_SystemService, "called");
if (event == nullptr) {
return ORBIS_SYSTEM_SERVICE_ERROR_PARAMETER;
}
std::lock_guard<std::mutex> lock(g_event_queue_mutex);
if (g_event_queue.empty()) {
return ORBIS_SYSTEM_SERVICE_ERROR_NO_EVENT;
}
*event = g_event_queue.front();
g_event_queue.pop();
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSystemServiceReenableMusicPlayer() {
LOG_ERROR(Lib_SystemService, "(STUBBED) called");
return ORBIS_OK;
@ -2412,6 +2424,11 @@ int PS4_SYSV_ABI Func_CB5E885E225F69F0() {
return ORBIS_OK;
}
void PushSystemServiceEvent(const OrbisSystemServiceEvent& event) {
std::lock_guard<std::mutex> lock(g_event_queue_mutex);
g_event_queue.push(event);
}
void RegisterlibSceSystemService(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("alZfRdr2RP8", "libSceAppMessaging", 1, "libSceSystemService", 1, 1,
sceAppMessagingClearEventFlag);

View File

@ -4,6 +4,8 @@
// https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/blob/master/include/orbis/_types/sys_service.h
#pragma once
#include <mutex>
#include <queue>
#include "common/types.h"
namespace Core::Loader {
@ -603,5 +605,7 @@ int PS4_SYSV_ABI sceSystemServiceReenableVoiceRecognition();
int PS4_SYSV_ABI Func_6B1CDB955F0EBD65();
int PS4_SYSV_ABI Func_CB5E885E225F69F0();
void PushSystemServiceEvent(const OrbisSystemServiceEvent& event);
void RegisterlibSceSystemService(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::SystemService

View File

@ -9,7 +9,7 @@
namespace Libraries::Videodec {
static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying
static constexpr u64 kMinimumMemorySize = 16_MB; ///> Fake minimum memory size for querying
int PS4_SYSV_ABI sceVideodecCreateDecoder(const OrbisVideodecConfigInfo* pCfgInfoIn,
const OrbisVideodecResourceInfo* pRsrcInfoIn,

View File

@ -10,7 +10,7 @@
namespace Libraries::Vdec2 {
static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying
static constexpr u64 kMinimumMemorySize = 16_MB; ///> Fake minimum memory size for querying
s32 PS4_SYSV_ABI
sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) {

View File

@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "core/libraries/web_browser_dialog/webbrowserdialog.h"
namespace Libraries::WebBrowserDialog {
s32 PS4_SYSV_ABI sceWebBrowserDialogClose() {
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceWebBrowserDialogGetEvent() {
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceWebBrowserDialogGetResult() {
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceWebBrowserDialogGetStatus() {
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceWebBrowserDialogInitialize() {
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceWebBrowserDialogNavigate() {
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceWebBrowserDialogOpen() {
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceWebBrowserDialogOpenForPredeterminedContent() {
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceWebBrowserDialogResetCookie() {
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceWebBrowserDialogSetCookie() {
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceWebBrowserDialogSetZoom() {
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceWebBrowserDialogTerminate() {
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceWebBrowserDialogUpdateStatus() {
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI Func_F2BE042771625F8C() {
LOG_ERROR(Lib_WebBrowserDialog, "(STUBBED) called");
return ORBIS_OK;
}
void RegisterlibSceWebBrowserDialog(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("PSK+Eik919Q", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1,
sceWebBrowserDialogClose);
LIB_FUNCTION("Wit4LjeoeX4", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1,
sceWebBrowserDialogGetEvent);
LIB_FUNCTION("vCaW0fgVQmc", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1,
sceWebBrowserDialogGetResult);
LIB_FUNCTION("CFTG6a8TjOU", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1,
sceWebBrowserDialogGetStatus);
LIB_FUNCTION("jqb7HntFQFc", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1,
sceWebBrowserDialogInitialize);
LIB_FUNCTION("uYELOMVnmNQ", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1,
sceWebBrowserDialogNavigate);
LIB_FUNCTION("FraP7debcdg", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1,
sceWebBrowserDialogOpen);
LIB_FUNCTION("O7dIZQrwVFY", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1,
sceWebBrowserDialogOpenForPredeterminedContent);
LIB_FUNCTION("Cya+jvTtPqg", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1,
sceWebBrowserDialogResetCookie);
LIB_FUNCTION("TZnDVkP91Rg", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1,
sceWebBrowserDialogSetCookie);
LIB_FUNCTION("RLhKBOoNyXY", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1,
sceWebBrowserDialogSetZoom);
LIB_FUNCTION("ocHtyBwHfys", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1,
sceWebBrowserDialogTerminate);
LIB_FUNCTION("h1dR-t5ISgg", "libSceWebBrowserDialog", 1, "libSceWebBrowserDialog", 1, 1,
sceWebBrowserDialogUpdateStatus);
LIB_FUNCTION("8r4EJ3FiX4w", "libSceWebBrowserDialogLimited", 1, "libSceWebBrowserDialog", 1, 1,
Func_F2BE042771625F8C);
};
} // namespace Libraries::WebBrowserDialog

View File

@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::WebBrowserDialog {
s32 PS4_SYSV_ABI sceWebBrowserDialogClose();
s32 PS4_SYSV_ABI sceWebBrowserDialogGetEvent();
s32 PS4_SYSV_ABI sceWebBrowserDialogGetResult();
s32 PS4_SYSV_ABI sceWebBrowserDialogGetStatus();
s32 PS4_SYSV_ABI sceWebBrowserDialogInitialize();
s32 PS4_SYSV_ABI sceWebBrowserDialogNavigate();
s32 PS4_SYSV_ABI sceWebBrowserDialogOpen();
s32 PS4_SYSV_ABI sceWebBrowserDialogOpenForPredeterminedContent();
s32 PS4_SYSV_ABI sceWebBrowserDialogResetCookie();
s32 PS4_SYSV_ABI sceWebBrowserDialogSetCookie();
s32 PS4_SYSV_ABI sceWebBrowserDialogSetZoom();
s32 PS4_SYSV_ABI sceWebBrowserDialogTerminate();
s32 PS4_SYSV_ABI sceWebBrowserDialogUpdateStatus();
s32 PS4_SYSV_ABI Func_F2BE042771625F8C();
void RegisterlibSceWebBrowserDialog(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::WebBrowserDialog

View File

@ -0,0 +1,183 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <condition_variable>
#include <mutex>
#include <stop_token>
#include <unordered_map>
#include <queue>
#include <zlib.h>
#include "common/logging/log.h"
#include "common/thread.h"
#include "core/libraries/kernel/threads.h"
#include "core/libraries/libs.h"
#include "core/libraries/zlib/zlib_error.h"
#include "core/libraries/zlib/zlib_sce.h"
namespace Libraries::Zlib {
struct InflateTask {
u64 request_id;
const void* src;
u32 src_length;
void* dst;
u32 dst_length;
};
struct InflateResult {
u32 length;
s32 status;
};
static Kernel::Thread task_thread;
static std::mutex mutex;
static std::queue<InflateTask> task_queue;
static std::condition_variable_any task_queue_cv;
static std::queue<u64> done_queue;
static std::condition_variable_any done_queue_cv;
static std::unordered_map<u64, InflateResult> results;
static u64 next_request_id;
void ZlibTaskThread(const std::stop_token& stop) {
Common::SetCurrentThreadName("shadPS4:ZlibTaskThread");
while (!stop.stop_requested()) {
InflateTask task;
{
// Lock and pop from the task queue, unless stop has been requested.
std::unique_lock lock(mutex);
if (!task_queue_cv.wait(lock, stop, [&] { return !task_queue.empty(); })) {
break;
}
task = task_queue.back();
task_queue.pop();
}
uLongf decompressed_length = task.dst_length;
const auto ret = uncompress(static_cast<Bytef*>(task.dst), &decompressed_length,
static_cast<const Bytef*>(task.src), task.src_length);
{
// Lock, insert the new result, and push the finished request ID to the done queue.
std::unique_lock lock(mutex);
results[task.request_id] = InflateResult{
.length = static_cast<u32>(decompressed_length),
.status = ret == Z_BUF_ERROR ? ORBIS_ZLIB_ERROR_NOSPACE
: ret == Z_OK ? ORBIS_OK
: ORBIS_ZLIB_ERROR_FATAL,
};
done_queue.push(task.request_id);
}
done_queue_cv.notify_one();
}
}
s32 PS4_SYSV_ABI sceZlibInitialize(const void* buffer, u32 length) {
LOG_INFO(Lib_Zlib, "called");
if (task_thread.Joinable()) {
return ORBIS_ZLIB_ERROR_ALREADY_INITIALIZED;
}
// Initialize with empty task data
task_queue = std::queue<InflateTask>();
done_queue = std::queue<u64>();
results.clear();
next_request_id = 1;
task_thread.Run([](const std::stop_token& stop) { ZlibTaskThread(stop); });
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceZlibInflate(const void* src, u32 src_len, void* dst, u32 dst_len,
u64* request_id) {
LOG_DEBUG(Lib_Zlib, "(STUBBED) called");
if (!task_thread.Joinable()) {
return ORBIS_ZLIB_ERROR_NOT_INITIALIZED;
}
if (!src || !src_len || !dst || !dst_len || !request_id || dst_len > 64_KB ||
dst_len % 2_KB != 0) {
return ORBIS_ZLIB_ERROR_INVALID;
}
{
std::unique_lock lock(mutex);
*request_id = next_request_id++;
task_queue.emplace(InflateTask{
.request_id = *request_id,
.src = src,
.src_length = src_len,
.dst = dst,
.dst_length = dst_len,
});
task_queue_cv.notify_one();
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceZlibWaitForDone(u64* request_id, const u32* timeout) {
LOG_DEBUG(Lib_Zlib, "(STUBBED) called");
if (!task_thread.Joinable()) {
return ORBIS_ZLIB_ERROR_NOT_INITIALIZED;
}
if (!request_id) {
return ORBIS_ZLIB_ERROR_INVALID;
}
{
// Pop from the done queue, unless the timeout is reached.
std::unique_lock lock(mutex);
const auto pred = [] { return !done_queue.empty(); };
if (timeout) {
if (!done_queue_cv.wait_for(lock, std::chrono::milliseconds(*timeout), pred)) {
return ORBIS_ZLIB_ERROR_TIMEDOUT;
}
} else {
done_queue_cv.wait(lock, pred);
}
*request_id = done_queue.back();
done_queue.pop();
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceZlibGetResult(const u64 request_id, u32* dst_length, s32* status) {
LOG_DEBUG(Lib_Zlib, "(STUBBED) called");
if (!task_thread.Joinable()) {
return ORBIS_ZLIB_ERROR_NOT_INITIALIZED;
}
if (!dst_length || !status) {
return ORBIS_ZLIB_ERROR_INVALID;
}
{
std::unique_lock lock(mutex);
if (!results.contains(request_id)) {
return ORBIS_ZLIB_ERROR_NOT_FOUND;
}
const auto result = results[request_id];
*dst_length = result.length;
*status = result.status;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceZlibFinalize() {
LOG_INFO(Lib_Zlib, "called");
if (!task_thread.Joinable()) {
return ORBIS_ZLIB_ERROR_NOT_INITIALIZED;
}
task_thread.Stop();
return ORBIS_OK;
}
void RegisterlibSceZlib(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("m1YErdIXCp4", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibInitialize);
LIB_FUNCTION("6na+Sa-B83w", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibFinalize);
LIB_FUNCTION("TLar1HULv1Q", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibInflate);
LIB_FUNCTION("uB8VlDD4e0s", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibWaitForDone);
LIB_FUNCTION("2eDcGHC0YaM", "libSceZlib", 1, "libSceZlib", 1, 1, sceZlibGetResult);
};
} // namespace Libraries::Zlib

View File

@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/libraries/error_codes.h"
// Zlib library
constexpr int ORBIS_ZLIB_ERROR_NOT_FOUND = 0x81120002;
constexpr int ORBIS_ZLIB_ERROR_BUSY = 0x8112000B;
constexpr int ORBIS_ZLIB_ERROR_FAULT = 0x8112000E;
constexpr int ORBIS_ZLIB_ERROR_INVALID = 0x81120016;
constexpr int ORBIS_ZLIB_ERROR_NOSPACE = 0x8112001C;
constexpr int ORBIS_ZLIB_ERROR_NOT_SUPPORTED = 0x81120025;
constexpr int ORBIS_ZLIB_ERROR_TIMEDOUT = 0x81120027;
constexpr int ORBIS_ZLIB_ERROR_NOT_INITIALIZED = 0x81120032;
constexpr int ORBIS_ZLIB_ERROR_ALREADY_INITIALIZED = 0x81120033;
constexpr int ORBIS_ZLIB_ERROR_FATAL = 0x811200FF;

View File

@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Zlib {
s32 PS4_SYSV_ABI sceZlibInitialize(const void* buffer, u32 length);
s32 PS4_SYSV_ABI sceZlibInflate(const void* src, u32 src_len, void* dst, u32 dst_len,
u64* request_id);
s32 PS4_SYSV_ABI sceZlibWaitForDone(u64* request_id, const u32* timeout);
s32 PS4_SYSV_ABI sceZlibGetResult(u64 request_id, u32* dst_length, s32* status);
s32 PS4_SYSV_ABI sceZlibFinalize();
void RegisterlibSceZlib(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Zlib

View File

@ -52,7 +52,7 @@ Linker::Linker() : memory{Memory::Instance()} {}
Linker::~Linker() = default;
void Linker::Execute() {
void Linker::Execute(const std::vector<std::string> args) {
if (Config::debugDump()) {
DebugDump();
}
@ -101,7 +101,7 @@ void Linker::Execute() {
memory->SetupMemoryRegions(fmem_size, use_extended_mem1, use_extended_mem2);
main_thread.Run([this, module](std::stop_token) {
main_thread.Run([this, module, args](std::stop_token) {
Common::SetCurrentThreadName("GAME_MainThread");
LoadSharedLibraries();
@ -109,6 +109,12 @@ void Linker::Execute() {
EntryParams params{};
params.argc = 1;
params.argv[0] = "eboot.bin";
if (!args.empty()) {
params.argc = args.size() + 1;
for (int i = 0; i < args.size() && i < 32; i++) {
params.argv[i + 1] = args[i].c_str();
}
}
params.entry_addr = module->GetEntryAddress();
RunMainEntry(&params);
});

View File

@ -49,7 +49,7 @@ class Linker;
struct EntryParams {
int argc;
u32 padding;
const char* argv[3];
const char* argv[33];
VAddr entry_addr;
};
@ -109,13 +109,16 @@ public:
void RelocateAnyImports(Module* m) {
Relocate(m);
const auto exports = m->GetExportModules();
for (auto& export_mod : exports) {
for (auto& module : m_modules) {
const auto imports = module->GetImportModules();
if (std::ranges::contains(imports, m->name, &ModuleInfo::name)) {
if (std::ranges::contains(imports, export_mod.name, &ModuleInfo::name)) {
Relocate(module.get());
}
}
}
}
void LoadSharedLibraries() {
for (auto& module : m_modules) {
@ -143,7 +146,7 @@ public:
void Relocate(Module* module);
bool Resolve(const std::string& name, Loader::SymbolType type, Module* module,
Loader::SymbolRecord* return_info);
void Execute();
void Execute(const std::vector<std::string> args = {});
void DebugDump();
private:

View File

@ -389,47 +389,57 @@ s32 MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) {
return UnmapMemoryImpl(virtual_addr, size);
}
s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) {
const auto it = FindVMA(virtual_addr);
const auto& vma_base = it->second;
ASSERT_MSG(vma_base.Contains(virtual_addr, size),
"Existing mapping does not contain requested unmap range");
const auto type = vma_base.type;
if (type == VMAType::Free) {
return ORBIS_OK;
}
u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma_base, u64 size) {
const auto vma_base_addr = vma_base.base;
const auto vma_base_size = vma_base.size;
const auto type = vma_base.type;
const auto phys_base = vma_base.phys_base;
const bool is_exec = vma_base.is_exec;
const auto start_in_vma = virtual_addr - vma_base_addr;
const auto adjusted_size =
vma_base_size - start_in_vma < size ? vma_base_size - start_in_vma : size;
const bool has_backing = type == VMAType::Direct || type == VMAType::File;
if (type == VMAType::Free) {
return adjusted_size;
}
if (type == VMAType::Direct || type == VMAType::Pooled) {
rasterizer->UnmapMemory(virtual_addr, size);
rasterizer->UnmapMemory(virtual_addr, adjusted_size);
}
if (type == VMAType::Flexible) {
flexible_usage -= size;
flexible_usage -= adjusted_size;
}
// Mark region as free and attempt to coalesce it with neighbours.
const auto new_it = CarveVMA(virtual_addr, size);
const auto new_it = CarveVMA(virtual_addr, adjusted_size);
auto& vma = new_it->second;
vma.type = VMAType::Free;
vma.prot = MemoryProt::NoAccess;
vma.phys_base = 0;
vma.disallow_merge = false;
vma.name = "";
MergeAdjacent(vma_map, new_it);
bool readonly_file = vma.prot == MemoryProt::CpuRead && type == VMAType::File;
const auto post_merge_it = MergeAdjacent(vma_map, new_it);
auto& post_merge_vma = post_merge_it->second;
bool readonly_file = post_merge_vma.prot == MemoryProt::CpuRead && type == VMAType::File;
if (type != VMAType::Reserved && type != VMAType::PoolReserved) {
// Unmap the memory region.
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base,
is_exec, has_backing, readonly_file);
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size,
phys_base, is_exec, has_backing, readonly_file);
TRACK_FREE(virtual_addr, "VMEM");
}
return adjusted_size;
}
s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, u64 size) {
u64 unmapped_bytes = 0;
do {
auto it = FindVMA(virtual_addr + unmapped_bytes);
auto& vma_base = it->second;
auto unmapped =
UnmapBytesFromEntry(virtual_addr + unmapped_bytes, vma_base, size - unmapped_bytes);
ASSERT_MSG(unmapped > 0, "Failed to unmap memory, progress is impossible");
unmapped_bytes += unmapped;
} while (unmapped_bytes < size);
return ORBIS_OK;
}
@ -529,8 +539,8 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags,
info->is_flexible.Assign(vma.type == VMAType::Flexible);
info->is_direct.Assign(vma.type == VMAType::Direct);
info->is_stack.Assign(vma.type == VMAType::Stack);
info->is_pooled.Assign(vma.type == VMAType::PoolReserved);
info->is_committed.Assign(vma.type == VMAType::Pooled);
info->is_pooled.Assign(vma.type == VMAType::PoolReserved || vma.type == VMAType::Pooled);
info->is_committed.Assign(vma.IsMapped());
vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size()));
if (vma.type == VMAType::Direct) {
const auto dmem_it = FindDmemArea(vma.phys_base);
@ -651,6 +661,12 @@ MemoryManager::VMAHandle MemoryManager::CarveVMA(VAddr virtual_addr, size_t size
const VAddr start_in_vma = virtual_addr - vma.base;
const VAddr end_in_vma = start_in_vma + size;
if (start_in_vma == 0 && size == vma.size) {
// if requsting the whole VMA, return it
return vma_handle;
}
ASSERT_MSG(end_in_vma <= vma.size, "Mapping cannot fit inside free region");
if (end_in_vma != vma.size) {

View File

@ -252,7 +252,9 @@ private:
DMemHandle Split(DMemHandle dmem_handle, size_t offset_in_area);
s32 UnmapMemoryImpl(VAddr virtual_addr, size_t size);
u64 UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma_base, u64 size);
s32 UnmapMemoryImpl(VAddr virtual_addr, u64 size);
private:
AddressSpace impl;

View File

@ -33,6 +33,7 @@
#include "core/libraries/ngs2/ngs2.h"
#include "core/libraries/np_trophy/np_trophy.h"
#include "core/libraries/rtc/rtc.h"
#include "core/libraries/save_data/save_backup.h"
#include "core/linker.h"
#include "core/memory.h"
#include "emulator.h"
@ -66,9 +67,9 @@ Emulator::Emulator() {
LOG_INFO(Config, "Vulkan vkValidation: {}", Config::vkValidationEnabled());
LOG_INFO(Config, "Vulkan vkValidationSync: {}", Config::vkValidationSyncEnabled());
LOG_INFO(Config, "Vulkan vkValidationGpu: {}", Config::vkValidationGpuEnabled());
LOG_INFO(Config, "Vulkan crashDiagnostics: {}", Config::vkCrashDiagnosticEnabled());
LOG_INFO(Config, "Vulkan hostMarkers: {}", Config::vkHostMarkersEnabled());
LOG_INFO(Config, "Vulkan guestMarkers: {}", Config::vkGuestMarkersEnabled());
LOG_INFO(Config, "Vulkan crashDiagnostics: {}", Config::getVkCrashDiagnosticEnabled());
LOG_INFO(Config, "Vulkan hostMarkers: {}", Config::getVkHostMarkersEnabled());
LOG_INFO(Config, "Vulkan guestMarkers: {}", Config::getVkGuestMarkersEnabled());
LOG_INFO(Config, "Vulkan rdocEnable: {}", Config::isRdocEnabled());
// Create stdin/stdout/stderr
@ -99,10 +100,22 @@ Emulator::~Emulator() {
Config::saveMainWindow(config_dir / "config.toml");
}
void Emulator::Run(const std::filesystem::path& file) {
void Emulator::Run(const std::filesystem::path& file, const std::vector<std::string> args) {
const auto eboot_name = file.filename().string();
auto game_folder = file.parent_path();
if (const auto game_folder_name = game_folder.filename().string();
game_folder_name.ends_with("-UPDATE")) {
// If an executable was launched from a separate update directory,
// use the base game directory as the game folder.
const auto base_name = game_folder_name.substr(0, game_folder_name.size() - 7);
const auto base_path = game_folder.parent_path() / base_name;
if (std::filesystem::is_directory(base_path)) {
game_folder = base_path;
}
}
// Applications expect to be run from /app0 so mount the file's parent path as app0.
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
const auto game_folder = file.parent_path();
mnt->Mount(game_folder, "/app0");
// Certain games may use /hostapp as well such as CUSA001100
mnt->Mount(game_folder, "/hostapp");
@ -152,6 +165,15 @@ void Emulator::Run(const std::filesystem::path& file) {
if (const auto raw_attributes = param_sfo->GetInteger("ATTRIBUTE")) {
psf_attributes.raw = *raw_attributes;
}
if (!args.empty()) {
int argc = std::min<int>(args.size(), 32);
for (int i = 0; i < argc; i++) {
LOG_INFO(Loader, "Game argument {}: {}", i, args[i]);
}
if (args.size() > 32) {
LOG_ERROR(Loader, "Too many game arguments, only passing the first 32");
}
}
}
const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png");
@ -214,7 +236,7 @@ void Emulator::Run(const std::filesystem::path& file) {
Libraries::InitHLELibs(&linker->GetHLESymbols());
// Load the module with the linker
const auto eboot_path = mnt->GetHostPath("/app0/" + file.filename().string());
const auto eboot_path = mnt->GetHostPath("/app0/" + eboot_name);
linker->LoadModule(eboot_path);
// check if we have system modules to load
@ -239,7 +261,7 @@ void Emulator::Run(const std::filesystem::path& file) {
}
#endif
linker->Execute();
linker->Execute(args);
window->InitTimers();
while (window->IsOpen()) {
@ -250,7 +272,7 @@ void Emulator::Run(const std::filesystem::path& file) {
UpdatePlayTime(id);
#endif
std::exit(0);
std::quick_exit(0);
}
void Emulator::LoadSystemModules(const std::string& game_serial) {

View File

@ -25,7 +25,7 @@ public:
Emulator();
~Emulator();
void Run(const std::filesystem::path& file);
void Run(const std::filesystem::path& file, const std::vector<std::string> args = {});
void UpdatePlayTime(const std::string& serial);
private:

View File

@ -186,7 +186,8 @@ ImGuiID NewFrame(bool is_reusing_frame) {
Sdl::NewFrame(is_reusing_frame);
ImGui::NewFrame();
ImGuiWindowFlags flags = ImGuiDockNodeFlags_PassthruCentralNode;
ImGuiWindowFlags flags =
ImGuiDockNodeFlags_PassthruCentralNode | ImGuiDockNodeFlags_AutoHideTabBar;
if (!DebugState.IsShowingDebugMenuBar()) {
flags |= ImGuiDockNodeFlags_NoTabBar;
}
@ -207,7 +208,7 @@ void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view,
return;
}
if (Config::vkHostMarkersEnabled()) {
if (Config::getVkHostMarkersEnabled()) {
cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{
.pLabelName = "ImGui Render",
});
@ -232,7 +233,7 @@ void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view,
cmdbuf.beginRendering(render_info);
Vulkan::RenderDrawData(*draw_data, cmdbuf);
cmdbuf.endRendering();
if (Config::vkHostMarkersEnabled()) {
if (Config::getVkHostMarkersEnabled()) {
cmdbuf.endDebugUtilsLabelEXT();
}
}

View File

@ -152,7 +152,7 @@ void WorkerLoop() {
g_job_list.pop_front();
g_job_list_mtx.unlock();
if (Config::vkCrashDiagnosticEnabled()) {
if (Config::getVkCrashDiagnosticEnabled()) {
// FIXME: Crash diagnostic hangs when building the command buffer here
continue;
}

View File

@ -10,6 +10,55 @@
namespace Input {
using Libraries::Pad::OrbisPadButtonDataOffset;
void State::OnButton(OrbisPadButtonDataOffset button, bool isPressed) {
if (isPressed) {
buttonsState |= button;
} else {
buttonsState &= ~button;
}
}
void State::OnAxis(Axis axis, int value) {
const auto toggle = [&](const auto button) {
if (value > 0) {
buttonsState |= button;
} else {
buttonsState &= ~button;
}
};
switch (axis) {
case Axis::TriggerLeft:
toggle(OrbisPadButtonDataOffset::L2);
break;
case Axis::TriggerRight:
toggle(OrbisPadButtonDataOffset::R2);
break;
default:
break;
}
axes[static_cast<int>(axis)] = value;
}
void State::OnTouchpad(int touchIndex, bool isDown, float x, float y) {
touchpad[touchIndex].state = isDown;
touchpad[touchIndex].x = static_cast<u16>(x * 1920);
touchpad[touchIndex].y = static_cast<u16>(y * 941);
}
void State::OnGyro(const float gyro[3]) {
angularVelocity.x = gyro[0];
angularVelocity.y = gyro[1];
angularVelocity.z = gyro[2];
}
void State::OnAccel(const float accel[3]) {
acceleration.x = accel[0];
acceleration.y = accel[1];
acceleration.z = accel[2];
}
GameController::GameController() {
m_states_num = 0;
m_last_state = State();
@ -75,45 +124,22 @@ void GameController::AddState(const State& state) {
m_states_num++;
}
void GameController::CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button,
bool is_pressed) {
void GameController::CheckButton(int id, OrbisPadButtonDataOffset button, bool is_pressed) {
std::scoped_lock lock{m_mutex};
auto state = GetLastState();
state.time = Libraries::Kernel::sceKernelGetProcessTime();
if (is_pressed) {
state.buttonsState |= button;
} else {
state.buttonsState &= ~button;
}
state.OnButton(button, is_pressed);
AddState(state);
}
void GameController::Axis(int id, Input::Axis axis, int value) {
using Libraries::Pad::OrbisPadButtonDataOffset;
std::scoped_lock lock{m_mutex};
auto state = GetLastState();
state.time = Libraries::Kernel::sceKernelGetProcessTime();
int axis_id = static_cast<int>(axis);
state.axes[axis_id] = value;
if (axis == Input::Axis::TriggerLeft) {
if (value > 0) {
state.buttonsState |= OrbisPadButtonDataOffset::L2;
} else {
state.buttonsState &= ~OrbisPadButtonDataOffset::L2;
}
}
if (axis == Input::Axis::TriggerRight) {
if (value > 0) {
state.buttonsState |= OrbisPadButtonDataOffset::R2;
} else {
state.buttonsState &= ~OrbisPadButtonDataOffset::R2;
}
}
state.OnAxis(axis, value);
AddState(state);
}
@ -124,9 +150,7 @@ void GameController::Gyro(int id, const float gyro[3]) {
state.time = Libraries::Kernel::sceKernelGetProcessTime();
// Update the angular velocity (gyro data)
state.angularVelocity.x = gyro[0]; // X-axis
state.angularVelocity.y = gyro[1]; // Y-axis
state.angularVelocity.z = gyro[2]; // Z-axis
state.OnGyro(gyro);
AddState(state);
}
@ -136,9 +160,7 @@ void GameController::Acceleration(int id, const float acceleration[3]) {
state.time = Libraries::Kernel::sceKernelGetProcessTime();
// Update the acceleration values
state.acceleration.x = acceleration[0]; // X-axis
state.acceleration.y = acceleration[1]; // Y-axis
state.acceleration.z = acceleration[2]; // Z-axis
state.OnAccel(acceleration);
AddState(state);
}
@ -211,62 +233,48 @@ void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceler
}
void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) {
if (m_sdl_gamepad != nullptr) {
SDL_SetGamepadLED(m_sdl_gamepad, r, g, b);
if (!m_engine) {
return;
}
std::scoped_lock _{m_mutex};
m_engine->SetLightBarRGB(r, g, b);
}
bool GameController::SetVibration(u8 smallMotor, u8 largeMotor) {
if (m_sdl_gamepad != nullptr) {
return SDL_RumbleGamepad(m_sdl_gamepad, (smallMotor / 255.0f) * 0xFFFF,
(largeMotor / 255.0f) * 0xFFFF, -1);
void GameController::SetVibration(u8 smallMotor, u8 largeMotor) {
if (!m_engine) {
return;
}
return true;
std::scoped_lock _{m_mutex};
m_engine->SetVibration(smallMotor, largeMotor);
}
void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, float y) {
if (touchIndex < 2) {
std::scoped_lock lock{m_mutex};
auto state = GetLastState();
state.time = Libraries::Kernel::sceKernelGetProcessTime();
state.touchpad[touchIndex].state = touchDown;
state.touchpad[touchIndex].x = static_cast<u16>(x * 1920);
state.touchpad[touchIndex].y = static_cast<u16>(y * 941);
state.time = Libraries::Kernel::sceKernelGetProcessTime();
state.OnTouchpad(touchIndex, touchDown, x, y);
AddState(state);
}
}
void GameController::TryOpenSDLController() {
if (m_sdl_gamepad == nullptr || !SDL_GamepadConnected(m_sdl_gamepad)) {
int gamepad_count;
SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count);
m_sdl_gamepad = gamepad_count > 0 ? SDL_OpenGamepad(gamepads[0]) : nullptr;
if (Config::getIsMotionControlsEnabled()) {
if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_GYRO, true)) {
gyro_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_GYRO);
LOG_INFO(Input, "Gyro initialized, poll rate: {}", gyro_poll_rate);
} else {
LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad");
}
if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_ACCEL, true)) {
accel_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_ACCEL);
LOG_INFO(Input, "Accel initialized, poll rate: {}", accel_poll_rate);
} else {
LOG_ERROR(Input, "Failed to initialize accel controls for gamepad");
void GameController::SetEngine(std::unique_ptr<Engine> engine) {
std::scoped_lock _{m_mutex};
m_engine = std::move(engine);
if (m_engine) {
m_engine->Init();
}
}
SDL_free(gamepads);
SetLightBarRGB(0, 0, 255);
}
Engine* GameController::GetEngine() {
return m_engine.get();
}
u32 GameController::Poll() {
std::scoped_lock lock{m_mutex};
if (m_connected) {
std::scoped_lock lock{m_mutex};
auto time = Libraries::Kernel::sceKernelGetProcessTime();
if (m_states_num == 0) {
auto diff = (time - m_last_state.time) / 1000;

View File

@ -3,12 +3,12 @@
#pragma once
#include <algorithm>
#include <memory>
#include <mutex>
#include "common/types.h"
#include "core/libraries/pad/pad.h"
struct SDL_Gamepad;
namespace Input {
enum class Axis {
@ -28,7 +28,14 @@ struct TouchpadEntry {
u16 y{};
};
struct State {
class State {
public:
void OnButton(Libraries::Pad::OrbisPadButtonDataOffset, bool);
void OnAxis(Axis, int);
void OnTouchpad(int touchIndex, bool isDown, float x, float y);
void OnGyro(const float[3]);
void OnAccel(const float[3]);
Libraries::Pad::OrbisPadButtonDataOffset buttonsState{};
u64 time = 0;
int axes[static_cast<int>(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0};
@ -38,9 +45,19 @@ struct State {
Libraries::Pad::OrbisFQuaternion orientation = {0.0f, 0.0f, 0.0f, 1.0f};
};
class Engine {
public:
virtual ~Engine() = default;
virtual void Init() = 0;
virtual void SetLightBarRGB(u8 r, u8 g, u8 b) = 0;
virtual void SetVibration(u8 smallMotor, u8 largeMotor) = 0;
virtual State ReadState() = 0;
virtual float GetAccelPollRate() const = 0;
virtual float GetGyroPollRate() const = 0;
};
inline int GetAxis(int min, int max, int value) {
int v = (255 * (value - min)) / (max - min);
return (v < 0 ? 0 : (v > 255 ? 255 : v));
return std::clamp((255 * (value - min)) / (max - min), 0, 255);
}
constexpr u32 MAX_STATES = 64;
@ -59,13 +76,12 @@ public:
void Gyro(int id, const float gyro[3]);
void Acceleration(int id, const float acceleration[3]);
void SetLightBarRGB(u8 r, u8 g, u8 b);
bool SetVibration(u8 smallMotor, u8 largeMotor);
void SetVibration(u8 smallMotor, u8 largeMotor);
void SetTouchpadState(int touchIndex, bool touchDown, float x, float y);
void TryOpenSDLController();
void SetEngine(std::unique_ptr<Engine>);
Engine* GetEngine();
u32 Poll();
float gyro_poll_rate;
float accel_poll_rate;
static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime,
@ -85,7 +101,7 @@ private:
std::array<State, MAX_STATES> m_states;
std::array<StateInternal, MAX_STATES> m_private;
SDL_Gamepad* m_sdl_gamepad = nullptr;
std::unique_ptr<Engine> m_engine = nullptr;
};
} // namespace Input

View File

@ -29,6 +29,7 @@ int main(int argc, char* argv[]) {
bool has_game_argument = false;
std::string game_path;
std::vector<std::string> game_args{};
// Map of argument strings to lambda functions
std::unordered_map<std::string, std::function<void(int&)>> arg_map = {
@ -37,6 +38,9 @@ int main(int argc, char* argv[]) {
std::cout << "Usage: shadps4 [options] <elf or eboot.bin path>\n"
"Options:\n"
" -g, --game <path|ID> Specify game path to launch\n"
" -- ... Parameters passed to the game ELF. "
"Needs to be at the end of the line, and everything after \"--\" is a "
"game argument.\n"
" -p, --patch <patch_file> Apply specified patch file\n"
" -f, --fullscreen <true|false> Specify window initial fullscreen "
"state. Does not overwrite the config file.\n"
@ -126,6 +130,21 @@ int main(int argc, char* argv[]) {
// Assume the last argument is the game file if not specified via -g/--game
game_path = argv[i];
has_game_argument = true;
} else if (std::string(argv[i]) == "--") {
if (i + 1 == argc) {
std::cerr << "Warning: -- is set, but no game arguments are added!\n";
break;
}
for (int j = i + 1; j < argc; j++) {
game_args.push_back(argv[j]);
}
break;
} else if (i + 1 < argc && std::string(argv[i + 1]) == "--") {
if (!has_game_argument) {
game_path = argv[i];
has_game_argument = true;
}
break;
} else {
std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n";
return 1;
@ -166,7 +185,7 @@ int main(int argc, char* argv[]) {
// Run the emulator with the resolved eboot path
Core::Emulator emulator;
emulator.Run(eboot_path);
emulator.Run(eboot_path, game_args);
return 0;
}

View File

@ -188,8 +188,12 @@ void CheatsPatches::setupUI() {
}
});
QPushButton* closeButton = new QPushButton(tr("Close"));
connect(closeButton, &QPushButton::clicked, [this]() { QWidget::close(); });
controlLayout->addWidget(downloadButton);
controlLayout->addWidget(deleteCheatButton);
controlLayout->addWidget(closeButton);
cheatsLayout->addLayout(controlLayout);
cheatsTab->setLayout(cheatsLayout);
@ -464,6 +468,8 @@ void CheatsPatches::onSaveButtonClicked() {
} else {
QMessageBox::information(this, tr("Success"), tr("Options saved successfully."));
}
QWidget::close();
}
QCheckBox* CheatsPatches::findCheckBoxByName(const QString& name) {

View File

@ -146,14 +146,14 @@ void CheckUpdate::CheckForUpdates(const bool showMessage) {
}
QString currentRev = (updateChannel == "Nightly")
? QString::fromStdString(Common::g_scm_rev).left(7)
? QString::fromStdString(Common::g_scm_rev)
: "v." + QString::fromStdString(Common::VERSION);
QString currentDate = Common::g_scm_date;
QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate);
latestDate = dateTime.isValid() ? dateTime.toString("yyyy-MM-dd HH:mm:ss") : "Unknown date";
if (latestRev == currentRev) {
if (latestRev == currentRev.left(7)) {
if (showMessage) {
QMessageBox::information(this, tr("Auto Updater"),
tr("Your version is already up to date!"));
@ -190,7 +190,7 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate,
QString("<p><b><br>" + tr("Update Channel") + ": </b>" + updateChannel + "<br><b>" +
tr("Current Version") + ":</b> %1 (%2)<br><b>" + tr("Latest Version") +
":</b> %3 (%4)</p><p>" + tr("Do you want to update?") + "</p>")
.arg(currentRev, currentDate, latestRev, latestDate);
.arg(currentRev.left(7), currentDate, latestRev, latestDate);
QLabel* updateLabel = new QLabel(updateText, this);
layout->addWidget(updateLabel);

View File

@ -38,17 +38,18 @@ GameGridFrame::GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get,
void GameGridFrame::onCurrentCellChanged(int currentRow, int currentColumn, int previousRow,
int previousColumn) {
cellClicked = true;
crtRow = currentRow;
crtColumn = currentColumn;
columnCnt = this->columnCount();
auto itemID = (crtRow * columnCnt) + currentColumn;
if (itemID > m_game_info->m_games.count() - 1) {
cellClicked = false;
validCellSelected = false;
BackgroundMusicPlayer::getInstance().stopMusic();
return;
}
cellClicked = true;
validCellSelected = true;
SetGridBackgroundImage(crtRow, crtColumn);
auto snd0Path = QString::fromStdString(m_game_info->m_games[itemID].snd0_path.string());

View File

@ -69,7 +69,7 @@ GameListFrame::GameListFrame(std::shared_ptr<GameInfoClass> game_info_get,
ListSortedAsc = true;
}
this->clearContents();
PopulateGameList();
PopulateGameList(false);
});
connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) {
@ -103,7 +103,7 @@ void GameListFrame::PlayBackgroundMusic(QTableWidgetItem* item) {
BackgroundMusicPlayer::getInstance().playMusic(snd0path);
}
void GameListFrame::PopulateGameList() {
void GameListFrame::PopulateGameList(bool isInitialPopulation) {
// Do not show status column if it is not enabled
this->setColumnHidden(2, !Config::getCompatibilityEnabled());
this->setColumnHidden(6, !Config::GetLoadGameSizeEnabled());
@ -111,6 +111,11 @@ void GameListFrame::PopulateGameList() {
this->setRowCount(m_game_info->m_games.size());
ResizeIcons(icon_size);
if (isInitialPopulation) {
SortNameAscending(1); // Column 1 = Name
ResizeIcons(icon_size);
}
for (int i = 0; i < m_game_info->m_games.size(); i++) {
SetTableItem(i, 1, QString::fromStdString(m_game_info->m_games[i].name));
SetTableItem(i, 3, QString::fromStdString(m_game_info->m_games[i].serial));

View File

@ -3,6 +3,9 @@
#pragma once
#include <algorithm> // std::transform
#include <cctype> // std::tolower
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
@ -43,7 +46,7 @@ private:
bool ListSortedAsc = true;
public:
void PopulateGameList();
void PopulateGameList(bool isInitialPopulation = true);
void ResizeIcons(int iconSize);
QImage backgroundImage;
@ -65,8 +68,12 @@ public:
static bool CompareStringsAscending(GameInfo a, GameInfo b, int columnIndex) {
switch (columnIndex) {
case 1:
return a.name < b.name;
case 1: {
std::string name_a = a.name, name_b = b.name;
std::transform(name_a.begin(), name_a.end(), name_a.begin(), ::tolower);
std::transform(name_b.begin(), name_b.end(), name_b.begin(), ::tolower);
return name_a < name_b;
}
case 2:
return a.compatibility.status < b.compatibility.status;
case 3:
@ -90,8 +97,12 @@ public:
static bool CompareStringsDescending(GameInfo a, GameInfo b, int columnIndex) {
switch (columnIndex) {
case 1:
return a.name > b.name;
case 1: {
std::string name_a = a.name, name_b = b.name;
std::transform(name_a.begin(), name_a.end(), name_a.begin(), ::tolower);
std::transform(name_b.begin(), name_b.end(), name_b.begin(), ::tolower);
return name_a > name_b;
}
case 2:
return a.compatibility.status > b.compatibility.status;
case 3:

View File

@ -41,8 +41,8 @@ public:
itemID = widget->currentRow() * widget->columnCount() + widget->currentColumn();
}
// Do not show the menu if an item is selected
if (itemID == -1) {
// Do not show the menu if no item is selected
if (itemID < 0 || itemID >= m_games.size()) {
return;
}
@ -52,10 +52,12 @@ public:
// "Open Folder..." submenu
QMenu* openFolderMenu = new QMenu(tr("Open Folder..."), widget);
QAction* openGameFolder = new QAction(tr("Open Game Folder"), widget);
QAction* openUpdateFolder = new QAction(tr("Open Update Folder"), widget);
QAction* openSaveDataFolder = new QAction(tr("Open Save Data Folder"), widget);
QAction* openLogFolder = new QAction(tr("Open Log Folder"), widget);
openFolderMenu->addAction(openGameFolder);
openFolderMenu->addAction(openUpdateFolder);
openFolderMenu->addAction(openSaveDataFolder);
openFolderMenu->addAction(openLogFolder);
@ -87,10 +89,12 @@ public:
QMenu* deleteMenu = new QMenu(tr("Delete..."), widget);
QAction* deleteGame = new QAction(tr("Delete Game"), widget);
QAction* deleteUpdate = new QAction(tr("Delete Update"), widget);
QAction* deleteSaveData = new QAction(tr("Delete Save Data"), widget);
QAction* deleteDLC = new QAction(tr("Delete DLC"), widget);
deleteMenu->addAction(deleteGame);
deleteMenu->addAction(deleteUpdate);
deleteMenu->addAction(deleteSaveData);
deleteMenu->addAction(deleteDLC);
menu.addMenu(deleteMenu);
@ -122,6 +126,18 @@ public:
QDesktopServices::openUrl(QUrl::fromLocalFile(folderPath));
}
if (selected == openUpdateFolder) {
QString open_update_path;
Common::FS::PathToQString(open_update_path, m_games[itemID].path);
open_update_path += "-UPDATE";
if (!std::filesystem::exists(Common::FS::PathFromQString(open_update_path))) {
QMessageBox::critical(nullptr, tr("Error"),
QString(tr("This game has no update folder to open!")));
} else {
QDesktopServices::openUrl(QUrl::fromLocalFile(open_update_path));
}
}
if (selected == openSaveDataFolder) {
QString userPath;
Common::FS::PathToQString(userPath,
@ -143,7 +159,7 @@ public:
PSF psf;
std::filesystem::path game_folder_path = m_games[itemID].path;
std::filesystem::path game_update_path = game_folder_path;
game_update_path += "UPDATE";
game_update_path += "-UPDATE";
if (std::filesystem::exists(game_update_path)) {
game_folder_path = game_update_path;
}
@ -238,6 +254,11 @@ public:
QString trophyPath, gameTrpPath;
Common::FS::PathToQString(trophyPath, m_games[itemID].serial);
Common::FS::PathToQString(gameTrpPath, m_games[itemID].path);
auto game_update_path = Common::FS::PathFromQString(gameTrpPath);
game_update_path += "-UPDATE";
if (std::filesystem::exists(game_update_path)) {
Common::FS::PathToQString(gameTrpPath, game_update_path);
}
TrophyViewer* trophyViewer = new TrophyViewer(trophyPath, gameTrpPath);
trophyViewer->show();
connect(widget->parent(), &QWidget::destroyed, trophyViewer,
@ -335,14 +356,18 @@ public:
clipboard->setText(combinedText);
}
if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC) {
if (selected == deleteGame || selected == deleteUpdate || selected == deleteDLC ||
selected == deleteSaveData) {
bool error = false;
QString folder_path, game_update_path, dlc_path;
QString folder_path, game_update_path, dlc_path, save_data_path;
Common::FS::PathToQString(folder_path, m_games[itemID].path);
game_update_path = folder_path + "-UPDATE";
Common::FS::PathToQString(
dlc_path, Config::getAddonInstallDir() /
Common::FS::PathFromQString(folder_path).parent_path().filename());
Common::FS::PathToQString(save_data_path,
Common::FS::GetUserPath(Common::FS::PathType::UserDir) /
"savedata/1" / m_games[itemID].serial);
QString message_type = tr("Game");
if (selected == deleteUpdate) {
@ -363,6 +388,15 @@ public:
folder_path = dlc_path;
message_type = tr("DLC");
}
} else if (selected == deleteSaveData) {
if (!std::filesystem::exists(Common::FS::PathFromQString(save_data_path))) {
QMessageBox::critical(nullptr, tr("Error"),
QString(tr("This game has no save data to delete!")));
error = true;
} else {
folder_path = save_data_path;
message_type = tr("Save Data");
}
}
if (!error) {
QString gameName = QString::fromStdString(m_games[itemID].name);
@ -374,7 +408,10 @@ public:
QMessageBox::Yes | QMessageBox::No);
if (reply == QMessageBox::Yes) {
dir.removeRecursively();
if (selected == deleteGame) {
widget->removeRow(itemID);
m_games.removeAt(itemID);
}
}
}
}

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QDir>
#include <QFileDialog>
@ -15,10 +16,11 @@
#include "install_dir_select.h"
InstallDirSelect::InstallDirSelect() : selected_dir() {
selected_dir = Config::getGameInstallDirs().empty() ? "" : Config::getGameInstallDirs().front();
auto install_dirs = Config::getGameInstallDirs();
selected_dir = install_dirs.empty() ? "" : install_dirs.front();
if (!Config::getGameInstallDirs().empty() && Config::getGameInstallDirs().size() == 1) {
reject();
if (!install_dirs.empty() && install_dirs.size() == 1) {
accept();
}
auto layout = new QVBoxLayout(this);
@ -53,6 +55,14 @@ QWidget* InstallDirSelect::SetupInstallDirList() {
vlayout->addWidget(m_path_list);
auto checkbox = new QCheckBox(tr("Install All Queued to Selected Folder"));
connect(checkbox, &QCheckBox::toggled, this, &InstallDirSelect::setUseForAllQueued);
vlayout->addWidget(checkbox);
auto checkbox2 = new QCheckBox(tr("Delete PKG File on Install"));
connect(checkbox2, &QCheckBox::toggled, this, &InstallDirSelect::setDeleteFileOnInstall);
vlayout->addWidget(checkbox2);
group->setLayout(vlayout);
return group;
}
@ -66,6 +76,14 @@ void InstallDirSelect::setSelectedDirectory(QListWidgetItem* item) {
}
}
void InstallDirSelect::setUseForAllQueued(bool enabled) {
use_for_all_queued = enabled;
}
void InstallDirSelect::setDeleteFileOnInstall(bool enabled) {
delete_file_on_install = enabled;
}
QWidget* InstallDirSelect::SetupDialogActions() {
auto actions = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);

View File

@ -22,9 +22,21 @@ public:
return selected_dir;
}
bool useForAllQueued() {
return use_for_all_queued;
}
bool deleteFileOnInstall() {
return delete_file_on_install;
}
private:
QWidget* SetupInstallDirList();
QWidget* SetupDialogActions();
void setSelectedDirectory(QListWidgetItem* item);
void setDeleteFileOnInstall(bool enabled);
void setUseForAllQueued(bool enabled);
std::filesystem::path selected_dir;
bool delete_file_on_install = false;
bool use_for_all_queued = false;
};

View File

@ -33,6 +33,7 @@ int main(int argc, char* argv[]) {
bool has_command_line_argument = argc > 1;
bool show_gui = false, has_game_argument = false;
std::string game_path;
std::vector<std::string> game_args{};
// Map of argument strings to lambda functions
std::unordered_map<std::string, std::function<void(int&)>> arg_map = {
@ -43,6 +44,9 @@ int main(int argc, char* argv[]) {
" No arguments: Opens the GUI.\n"
" -g, --game <path|ID> Specify <eboot.bin or elf path> or "
"<game ID (CUSAXXXXX)> to launch\n"
" -- ... Parameters passed to the game ELF. "
"Needs to be at the end of the line, and everything after \"--\" is a "
"game argument.\n"
" -p, --patch <patch_file> Apply specified patch file\n"
" -s, --show-gui Show the GUI\n"
" -f, --fullscreen <true|false> Specify window initial fullscreen "
@ -131,6 +135,20 @@ int main(int argc, char* argv[]) {
// Assume the last argument is the game file if not specified via -g/--game
game_path = argv[i];
has_game_argument = true;
} else if (std::string(argv[i]) == "--") {
if (i + 1 == argc) {
std::cerr << "Warning: -- is set, but no game arguments are added!\n";
break;
}
for (int j = i + 1; j < argc; j++) {
game_args.push_back(argv[j]);
}
break;
} else if (i + 1 < argc && std::string(argv[i + 1]) == "--") {
if (!has_game_argument) {
game_path = argv[i];
has_game_argument = true;
}
} else {
std::cerr << "Unknown argument: " << cur_arg << ", see --help for info.\n";
return 1;
@ -181,7 +199,7 @@ int main(int argc, char* argv[]) {
// Run the emulator with the resolved game path
Core::Emulator emulator;
emulator.Run(game_file_path.string());
emulator.Run(game_file_path.string(), game_args);
if (!show_gui) {
return 0; // Exit after running the emulator without showing the GUI
}

View File

@ -251,6 +251,12 @@ void MainWindow::CreateConnects() {
}
});
connect(ui->shadFolderAct, &QAction::triggered, this, [this]() {
QString userPath;
Common::FS::PathToQString(userPath, Common::FS::GetUserPath(Common::FS::PathType::UserDir));
QDesktopServices::openUrl(QUrl::fromLocalFile(userPath));
});
connect(ui->playButton, &QPushButton::clicked, this, &MainWindow::StartGame);
connect(m_game_grid_frame.get(), &QTableWidget::cellDoubleClicked, this,
&MainWindow::StartGame);
@ -729,9 +735,20 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int
return;
}
auto category = psf.GetString("CATEGORY");
if (!use_for_all_queued || pkgNum == 1) {
InstallDirSelect ids;
ids.exec();
auto game_install_dir = ids.getSelectedDirectory();
const auto selected = ids.exec();
if (selected == QDialog::Rejected) {
return;
}
last_install_dir = ids.getSelectedDirectory();
delete_file_on_install = ids.deleteFileOnInstall();
use_for_all_queued = ids.useForAllQueued();
}
std::filesystem::path game_install_dir = last_install_dir;
auto game_folder_path = game_install_dir / pkg.GetTitleID();
QString pkgType = QString::fromStdString(pkg.GetPkgFlags());
bool use_game_update = pkgType.contains("PATCH") && Config::getSeparateUpdateEnabled();
@ -883,8 +900,14 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int
if (pkgNum == nPkg) {
QString path;
Common::FS::PathToQString(path, game_install_dir);
QIcon windowIcon(
Common::FS::PathToUTF8String(game_folder_path / "sce_sys/icon0.png")
.c_str());
QMessageBox extractMsgBox(this);
extractMsgBox.setWindowTitle(tr("Extraction Finished"));
if (!windowIcon.isNull()) {
extractMsgBox.setWindowIcon(windowIcon);
}
extractMsgBox.setText(
QString(tr("Game successfully installed at %1")).arg(path));
extractMsgBox.addButton(QMessageBox::Ok);
@ -898,6 +921,9 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int
});
extractMsgBox.exec();
}
if (delete_file_on_install) {
std::filesystem::remove(file);
}
});
connect(&dialog, &QProgressDialog::canceled, [&]() { futureWatcher.cancel(); });
connect(&futureWatcher, &QFutureWatcher<void>::progressValueChanged, &dialog,
@ -992,6 +1018,7 @@ QIcon MainWindow::RecolorIcon(const QIcon& icon, bool isWhite) {
void MainWindow::SetUiIcons(bool isWhite) {
ui->bootInstallPkgAct->setIcon(RecolorIcon(ui->bootInstallPkgAct->icon(), isWhite));
ui->bootGameAct->setIcon(RecolorIcon(ui->bootGameAct->icon(), isWhite));
ui->shadFolderAct->setIcon(RecolorIcon(ui->shadFolderAct->icon(), isWhite));
ui->exitAct->setIcon(RecolorIcon(ui->exitAct->icon(), isWhite));
#ifdef ENABLE_UPDATER
ui->updaterAct->setIcon(RecolorIcon(ui->updaterAct->icon(), isWhite));

View File

@ -123,4 +123,8 @@ protected:
}
void resizeEvent(QResizeEvent* event) override;
std::filesystem::path last_install_dir = "";
bool delete_file_on_install = false;
bool use_for_all_queued = false;
};

View File

@ -12,6 +12,7 @@ public:
QAction* bootInstallPkgAct;
QAction* bootGameAct;
QAction* addElfFolderAct;
QAction* shadFolderAct;
QAction* exitAct;
QAction* showGameListAct;
QAction* refreshGameListAct;
@ -89,6 +90,9 @@ public:
addElfFolderAct = new QAction(MainWindow);
addElfFolderAct->setObjectName("addElfFolderAct");
addElfFolderAct->setIcon(QIcon(":images/folder_icon.png"));
shadFolderAct = new QAction(MainWindow);
shadFolderAct->setObjectName("shadFolderAct");
shadFolderAct->setIcon(QIcon(":images/folder_icon.png"));
exitAct = new QAction(MainWindow);
exitAct->setObjectName("exitAct");
exitAct->setIcon(QIcon(":images/exit_icon.png"));
@ -274,7 +278,9 @@ public:
menuBar->addAction(menuHelp->menuAction());
menuFile->addAction(bootInstallPkgAct);
menuFile->addAction(bootGameAct);
menuFile->addSeparator();
menuFile->addAction(addElfFolderAct);
menuFile->addAction(shadFolderAct);
menuFile->addSeparator();
menuFile->addAction(menuRecent->menuAction());
menuFile->addSeparator();
@ -333,6 +339,8 @@ public:
"MainWindow", "Install application from a .pkg file", nullptr));
#endif // QT_CONFIG(tooltip)
menuRecent->setTitle(QCoreApplication::translate("MainWindow", "Recent Games", nullptr));
shadFolderAct->setText(
QCoreApplication::translate("MainWindow", "Open shadPS4 Folder", nullptr));
exitAct->setText(QCoreApplication::translate("MainWindow", "Exit", nullptr));
#if QT_CONFIG(tooltip)
exitAct->setToolTip(QCoreApplication::translate("MainWindow", "Exit shadPS4", nullptr));

View File

@ -65,6 +65,7 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
: QDialog(parent), ui(new Ui::SettingsDialog) {
ui->setupUi(this);
ui->tabWidgetSettings->setUsesScrollButtons(false);
initialHeight = this->height();
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
@ -150,7 +151,6 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
});
#else
ui->updaterGroupBox->setVisible(false);
ui->GUIgroupBox->setMaximumSize(265, 16777215);
#endif
connect(ui->updateCompatibilityButton, &QPushButton::clicked, this,
[this, parent, m_compat_info]() {
@ -169,6 +169,11 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
});
}
// Gui TAB
{
connect(ui->chooseHomeTabComboBox, &QComboBox::currentTextChanged, this,
[](const QString& hometab) { Config::setChooseHomeTab(hometab.toStdString()); });
}
// Input TAB
{
connect(ui->hideCursorComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
@ -202,6 +207,21 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
delete selected_item;
}
});
connect(ui->browseButton, &QPushButton::clicked, this, [this]() {
const auto save_data_path = Config::GetSaveDataPath();
QString initial_path;
Common::FS::PathToQString(initial_path, save_data_path);
QString save_data_path_string =
QFileDialog::getExistingDirectory(this, tr("Directory to save data"), initial_path);
auto file_path = Common::FS::PathFromQString(save_data_path_string);
if (!file_path.empty()) {
Config::setSaveDataPath(file_path);
ui->currentSaveDataPath->setText(save_data_path_string);
}
});
}
// DEBUG TAB
@ -231,7 +251,7 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
#ifdef ENABLE_UPDATER
ui->updaterGroupBox->installEventFilter(this);
#endif
ui->GUIgroupBox->installEventFilter(this);
ui->GUIMusicGroupBox->installEventFilter(this);
ui->disableTrophycheckBox->installEventFilter(this);
ui->enableCompatibilityCheckBox->installEventFilter(this);
ui->checkCompatibilityOnStartupCheckBox->installEventFilter(this);
@ -256,11 +276,20 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
ui->addFolderButton->installEventFilter(this);
ui->removeFolderButton->installEventFilter(this);
ui->saveDataGroupBox->installEventFilter(this);
ui->currentSaveDataPath->installEventFilter(this);
ui->browseButton->installEventFilter(this);
// Debug
ui->debugDump->installEventFilter(this);
ui->vkValidationCheckBox->installEventFilter(this);
ui->vkSyncValidationCheckBox->installEventFilter(this);
ui->rdocCheckBox->installEventFilter(this);
ui->crashDiagnosticsCheckBox->installEventFilter(this);
ui->guestMarkersCheckBox->installEventFilter(this);
ui->hostMarkersCheckBox->installEventFilter(this);
ui->collectShaderCheckBox->installEventFilter(this);
ui->copyGPUBuffersCheckBox->installEventFilter(this);
}
}
@ -286,6 +315,11 @@ void SettingsDialog::LoadValuesFromConfig() {
const QVector<int> languageIndexes = {21, 23, 14, 6, 18, 1, 12, 22, 2, 4, 25, 24, 29, 5, 0, 9,
15, 16, 17, 7, 26, 8, 11, 20, 3, 13, 27, 10, 19, 30, 28};
const auto save_data_path = Config::GetSaveDataPath();
QString save_data_path_string;
Common::FS::PathToQString(save_data_path_string, save_data_path);
ui->currentSaveDataPath->setText(save_data_path_string);
ui->consoleLanguageComboBox->setCurrentIndex(
std::distance(languageIndexes.begin(),
std::find(languageIndexes.begin(), languageIndexes.end(),
@ -331,6 +365,15 @@ void SettingsDialog::LoadValuesFromConfig() {
ui->vkSyncValidationCheckBox->setChecked(
toml::find_or<bool>(data, "Vulkan", "validation_sync", false));
ui->rdocCheckBox->setChecked(toml::find_or<bool>(data, "Vulkan", "rdocEnable", false));
ui->crashDiagnosticsCheckBox->setChecked(
toml::find_or<bool>(data, "Vulkan", "crashDiagnostic", false));
ui->guestMarkersCheckBox->setChecked(
toml::find_or<bool>(data, "Vulkan", "guestMarkers", false));
ui->hostMarkersCheckBox->setChecked(toml::find_or<bool>(data, "Vulkan", "hostMarkers", false));
ui->copyGPUBuffersCheckBox->setChecked(
toml::find_or<bool>(data, "GPU", "copyGPUBuffers", false));
ui->collectShaderCheckBox->setChecked(
toml::find_or<bool>(data, "Debug", "CollectShader", false));
ui->enableCompatibilityCheckBox->setChecked(
toml::find_or<bool>(data, "General", "compatibilityEnabled", false));
ui->checkCompatibilityOnStartupCheckBox->setChecked(
@ -349,6 +392,15 @@ void SettingsDialog::LoadValuesFromConfig() {
ui->updateComboBox->setCurrentText(QString::fromStdString(updateChannel));
#endif
std::string chooseHomeTab = toml::find_or<std::string>(data, "General", "chooseHomeTab", "");
ui->chooseHomeTabComboBox->setCurrentText(QString::fromStdString(chooseHomeTab));
QStringList tabNames = {tr("General"), tr("GUI"), tr("Graphics"), tr("User"),
tr("Input"), tr("Paths"), tr("Debug")};
QString chooseHomeTabQString = QString::fromStdString(chooseHomeTab);
int indexTab = tabNames.indexOf(chooseHomeTabQString);
indexTab = (indexTab == -1) ? 0 : indexTab;
ui->tabWidgetSettings->setCurrentIndex(indexTab);
QString backButtonBehavior = QString::fromStdString(
toml::find_or<std::string>(data, "Input", "backButtonBehavior", "left"));
int index = ui->backButtonBehaviorComboBox->findData(backButtonBehavior);
@ -452,8 +504,8 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) {
} else if (elementName == "updaterGroupBox") {
text = tr("updaterGroupBox");
#endif
} else if (elementName == "GUIgroupBox") {
text = tr("GUIgroupBox");
} else if (elementName == "GUIMusicGroupBox") {
text = tr("GUIMusicGroupBox");
} else if (elementName == "disableTrophycheckBox") {
text = tr("disableTrophycheckBox");
} else if (elementName == "enableCompatibilityCheckBox") {
@ -497,6 +549,13 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) {
text = tr("removeFolderButton");
}
// Save Data
if (elementName == "saveDataGroupBox" || elementName == "currentSaveDataPath") {
text = tr("saveDataBox");
} else if (elementName == "browseButton") {
text = tr("browseButton");
}
// Debug
if (elementName == "debugDump") {
text = tr("debugDump");
@ -506,6 +565,16 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) {
text = tr("vkSyncValidationCheckBox");
} else if (elementName == "rdocCheckBox") {
text = tr("rdocCheckBox");
} else if (elementName == "crashDiagnosticsCheckBox") {
text = tr("crashDiagnosticsCheckBox");
} else if (elementName == "guestMarkersCheckBox") {
text = tr("guestMarkersCheckBox");
} else if (elementName == "hostMarkersCheckBox") {
text = tr("hostMarkersCheckBox");
} else if (elementName == "copyGPUBuffersCheckBox") {
text = tr("copyGPUBuffersCheckBox");
} else if (elementName == "collectShaderCheckBox") {
text = tr("collectShaderCheckBox");
}
ui->descriptionText->setText(text.replace("\\n", "\n"));
@ -522,22 +591,6 @@ bool SettingsDialog::eventFilter(QObject* obj, QEvent* event) {
} else {
ui->descriptionText->setText(defaultTextEdit);
}
// if the text exceeds the size of the box, it will increase the size
QRect currentGeometry = this->geometry();
int newWidth = currentGeometry.width();
int documentHeight = ui->descriptionText->document()->size().height();
int visibleHeight = ui->descriptionText->viewport()->height();
if (documentHeight > visibleHeight) {
ui->descriptionText->setMaximumSize(16777215, 110);
this->setGeometry(currentGeometry.x(), currentGeometry.y(), newWidth,
currentGeometry.height() + 40);
} else {
ui->descriptionText->setMaximumSize(16777215, 70);
this->setGeometry(currentGeometry.x(), currentGeometry.y(), newWidth,
initialHeight);
}
return true;
}
}
@ -575,8 +628,14 @@ void SettingsDialog::UpdateSettings() {
Config::setVkValidation(ui->vkValidationCheckBox->isChecked());
Config::setVkSyncValidation(ui->vkSyncValidationCheckBox->isChecked());
Config::setRdocEnabled(ui->rdocCheckBox->isChecked());
Config::setVkHostMarkersEnabled(ui->hostMarkersCheckBox->isChecked());
Config::setVkGuestMarkersEnabled(ui->guestMarkersCheckBox->isChecked());
Config::setVkCrashDiagnosticEnabled(ui->crashDiagnosticsCheckBox->isChecked());
Config::setCollectShaderForDebug(ui->collectShaderCheckBox->isChecked());
Config::setCopyGPUCmdBuffers(ui->copyGPUBuffersCheckBox->isChecked());
Config::setAutoUpdate(ui->updateCheckBox->isChecked());
Config::setUpdateChannel(ui->updateComboBox->currentText().toStdString());
Config::setChooseHomeTab(ui->chooseHomeTabComboBox->currentText().toStdString());
Config::setCompatibilityEnabled(ui->enableCompatibilityCheckBox->isChecked());
Config::setCheckCompatibilityOnStartup(ui->checkCompatibilityOnStartupCheckBox->isChecked());

File diff suppressed because it is too large Load Diff

View File

@ -247,6 +247,10 @@
<source>Recent Games</source>
<translation>الألعاب الأخيرة</translation>
</message>
<message>
<source>Open shadPS4 Folder</source>
<translation>Open shadPS4 Folder</translation>
</message>
<message>
<source>Exit</source>
<translation>خروج</translation>
@ -536,10 +540,18 @@
<source>Enable Fullscreen</source>
<translation>تمكين ملء الشاشة</translation>
</message>
<message>
<source>Fullscreen Mode</source>
<translation>وضع ملء الشاشة</translation>
</message>
<message>
<source>Enable Separate Update Folder</source>
<translation>Enable Separate Update Folder</translation>
</message>
<message>
<source>Default tab when opening settings</source>
<translation>علامة التبويب الافتراضية عند فتح الإعدادات</translation>
</message>
<message>
<source>Show Game Size In List</source>
<translation>عرض حجم اللعبة في القائمة</translation>
@ -616,6 +628,14 @@
<source>Graphics</source>
<translation>الرسومات</translation>
</message>
<message>
<source>Gui</source>
<translation>واجهة</translation>
</message>
<message>
<source>User</source>
<translation>مستخدم</translation>
</message>
<message>
<source>Graphics Device</source>
<translation>جهاز الرسومات</translation>
@ -680,6 +700,26 @@
<source>Enable RenderDoc Debugging</source>
<translation>RenderDoc تمكين تصحيح أخطاء</translation>
</message>
<message>
<source>Enable Crash Diagnostics</source>
<translation>Enable Crash Diagnostics</translation>
</message>
<message>
<source>Collect Shaders</source>
<translation>Collect Shaders</translation>
</message>
<message>
<source>Copy GPU Buffers</source>
<translation>Copy GPU Buffers</translation>
</message>
<message>
<source>Host Debug Markers</source>
<translation>Host Debug Markers</translation>
</message>
<message>
<source>Guest Debug Markers</source>
<translation>Guest Debug Markers</translation>
</message>
<message>
<source>Update</source>
<translation>تحديث</translation>
@ -700,6 +740,10 @@
<source>GUI Settings</source>
<translation>إعدادات الواجهة</translation>
</message>
<message>
<source>Title Music</source>
<translation>Title Music</translation>
</message>
<message>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
@ -801,7 +845,7 @@
<translation>تحديث: Release: إصدارات رسمية تصدر شهريًا، قد تكون قديمة بعض الشيء، لكنها أكثر استقرارًا واختبارًا. Nightly: إصدارات تطوير تحتوي على أحدث الميزات والإصلاحات، لكنها قد تحتوي على أخطاء وأقل استقرارًا.</translation>
</message>
<message>
<source>GUIgroupBox</source>
<source>GUIMusicGroupBox</source>
<translation>تشغيل موسيقى العنوان:\nإذا كانت اللعبة تدعم ذلك، قم بتمكين تشغيل موسيقى خاصة عند اختيار اللعبة في واجهة المستخدم.</translation>
</message>
<message>
@ -908,6 +952,26 @@
<source>rdocCheckBox</source>
<translation>تمكين تصحيح RenderDoc:\nإذا تم التمكين، سيوفر المحاكي توافقًا مع Renderdoc لالتقاط وتحليل الإطار الذي يتم عرضه حاليًا.</translation>
</message>
<message>
<source>collectShaderCheckBox</source>
<translation>Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10).</translation>
</message>
<message>
<source>crashDiagnosticsCheckBox</source>
<translation>Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work.</translation>
</message>
<message>
<source>copyGPUBuffersCheckBox</source>
<translation>Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes.</translation>
</message>
<message>
<source>hostMarkersCheckBox</source>
<translation>Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</translation>
</message>
<message>
<source>guestMarkersCheckBox</source>
<translation>Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</translation>
</message>
</context>
<context>
<name>CheatsPatches</name>

View File

@ -247,6 +247,10 @@
<source>Recent Games</source>
<translation>Recent Games</translation>
</message>
<message>
<source>Open shadPS4 Folder</source>
<translation>Open shadPS4 Folder</translation>
</message>
<message>
<source>Exit</source>
<translation>Exit</translation>
@ -536,10 +540,18 @@
<source>Enable Fullscreen</source>
<translation>Enable Fullscreen</translation>
</message>
<message>
<source>Fullscreen Mode</source>
<translation>Fuldskærmstilstand</translation>
</message>
<message>
<source>Enable Separate Update Folder</source>
<translation>Enable Separate Update Folder</translation>
</message>
<message>
<source>Default tab when opening settings</source>
<translation>Standardfaneblad ved åbning af indstillinger</translation>
</message>
<message>
<source>Show Game Size In List</source>
<translation>Vis vis spilstørrelse i listen</translation>
@ -616,6 +628,14 @@
<source>Graphics</source>
<translation>Graphics</translation>
</message>
<message>
<source>Gui</source>
<translation>Interface</translation>
</message>
<message>
<source>User</source>
<translation>Bruger</translation>
</message>
<message>
<source>Graphics Device</source>
<translation>Graphics Device</translation>
@ -680,6 +700,26 @@
<source>Enable RenderDoc Debugging</source>
<translation>Enable RenderDoc Debugging</translation>
</message>
<message>
<source>Enable Crash Diagnostics</source>
<translation>Enable Crash Diagnostics</translation>
</message>
<message>
<source>Collect Shaders</source>
<translation>Collect Shaders</translation>
</message>
<message>
<source>Copy GPU Buffers</source>
<translation>Copy GPU Buffers</translation>
</message>
<message>
<source>Host Debug Markers</source>
<translation>Host Debug Markers</translation>
</message>
<message>
<source>Guest Debug Markers</source>
<translation>Guest Debug Markers</translation>
</message>
<message>
<source>Update</source>
<translation>Opdatering</translation>
@ -700,6 +740,10 @@
<source>GUI Settings</source>
<translation>GUI-Indstillinger</translation>
</message>
<message>
<source>Title Music</source>
<translation>Title Music</translation>
</message>
<message>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
@ -801,7 +845,7 @@
<translation>Opdatering:\nRelease: Officielle builds, der frigives månedligt, som kan være meget ældre, men mere stabile og testet.\nNightly: Udviklerbuilds med de nyeste funktioner og rettelser, men som kan indeholde fejl og være mindre stabile.</translation>
</message>
<message>
<source>GUIgroupBox</source>
<source>GUIMusicGroupBox</source>
<translation>Titelsmusikafspilning:\nHvis spillet understøtter det, aktiver speciel musik, når spillet vælges i brugergrænsefladen.</translation>
</message>
<message>
@ -908,6 +952,26 @@
<source>rdocCheckBox</source>
<translation>Aktiver RenderDoc-fejlfinding:\nHvis aktiveret, giver det emulatoren mulighed for kompatibilitet med Renderdoc til at fange og analysere det aktuelle gengivne billede.</translation>
</message>
<message>
<source>collectShaderCheckBox</source>
<translation>Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10).</translation>
</message>
<message>
<source>crashDiagnosticsCheckBox</source>
<translation>Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work.</translation>
</message>
<message>
<source>copyGPUBuffersCheckBox</source>
<translation>Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes.</translation>
</message>
<message>
<source>hostMarkersCheckBox</source>
<translation>Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</translation>
</message>
<message>
<source>guestMarkersCheckBox</source>
<translation>Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</translation>
</message>
</context>
<context>
<name>CheatsPatches</name>

View File

@ -247,6 +247,10 @@
<source>Recent Games</source>
<translation>Zuletzt gespielt</translation>
</message>
<message>
<source>Open shadPS4 Folder</source>
<translation>Open shadPS4 Folder</translation>
</message>
<message>
<source>Exit</source>
<translation>Beenden</translation>
@ -536,10 +540,18 @@
<source>Enable Fullscreen</source>
<translation>Vollbild aktivieren</translation>
</message>
<message>
<source>Fullscreen Mode</source>
<translation>Vollbildmodus</translation>
</message>
<message>
<source>Enable Separate Update Folder</source>
<translation>Enable Separate Update Folder</translation>
</message>
<message>
<source>Default tab when opening settings</source>
<translation>Standardregisterkarte beim Öffnen der Einstellungen</translation>
</message>
<message>
<source>Show Game Size In List</source>
<translation>Zeigen Sie die Spielgröße in der Liste</translation>
@ -616,6 +628,14 @@
<source>Graphics</source>
<translation>Grafik</translation>
</message>
<message>
<source>Gui</source>
<translation>Benutzeroberfläche</translation>
</message>
<message>
<source>User</source>
<translation>Benutzer</translation>
</message>
<message>
<source>Graphics Device</source>
<translation>Grafikgerät</translation>
@ -680,6 +700,26 @@
<source>Enable RenderDoc Debugging</source>
<translation>RenderDoc-Debugging aktivieren</translation>
</message>
<message>
<source>Enable Crash Diagnostics</source>
<translation>Enable Crash Diagnostics</translation>
</message>
<message>
<source>Collect Shaders</source>
<translation>Collect Shaders</translation>
</message>
<message>
<source>Copy GPU Buffers</source>
<translation>Copy GPU Buffers</translation>
</message>
<message>
<source>Host Debug Markers</source>
<translation>Host Debug Markers</translation>
</message>
<message>
<source>Guest Debug Markers</source>
<translation>Guest Debug Markers</translation>
</message>
<message>
<source>Update</source>
<translation>Aktualisieren</translation>
@ -700,6 +740,10 @@
<source>GUI Settings</source>
<translation>GUI-Einstellungen</translation>
</message>
<message>
<source>Title Music</source>
<translation>Title Music</translation>
</message>
<message>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
@ -801,7 +845,7 @@
<translation>Update:\nRelease: Offizielle Builds, die monatlich veröffentlicht werden, können viel älter sein, aber stabiler und getestet.\nNightly: Entwickler-Builds, die die neuesten Funktionen und Fehlerbehebungen enthalten, aber Fehler enthalten und weniger stabil sein können.</translation>
</message>
<message>
<source>GUIgroupBox</source>
<source>GUIMusicGroupBox</source>
<translation>Wiedergabe der Titelmusik:\nWenn das Spiel dies unterstützt, wird beim Auswählen des Spiels in der Benutzeroberfläche spezielle Musik abgespielt.</translation>
</message>
<message>
@ -908,6 +952,26 @@
<source>rdocCheckBox</source>
<translation>RenderDoc-Debugging aktivieren:\nWenn aktiviert, bietet der Emulator Kompatibilität mit Renderdoc zur Erfassung und Analyse des aktuell gerenderten Frames.</translation>
</message>
<message>
<source>collectShaderCheckBox</source>
<translation>Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10).</translation>
</message>
<message>
<source>crashDiagnosticsCheckBox</source>
<translation>Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work.</translation>
</message>
<message>
<source>copyGPUBuffersCheckBox</source>
<translation>Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes.</translation>
</message>
<message>
<source>hostMarkersCheckBox</source>
<translation>Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</translation>
</message>
<message>
<source>guestMarkersCheckBox</source>
<translation>Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</translation>
</message>
</context>
<context>
<name>CheatsPatches</name>

View File

@ -247,6 +247,10 @@
<source>Recent Games</source>
<translation>Recent Games</translation>
</message>
<message>
<source>Open shadPS4 Folder</source>
<translation>Open shadPS4 Folder</translation>
</message>
<message>
<source>Exit</source>
<translation>Exit</translation>
@ -536,10 +540,18 @@
<source>Enable Fullscreen</source>
<translation>Enable Fullscreen</translation>
</message>
<message>
<source>Fullscreen Mode</source>
<translation>Λειτουργία Πλήρους Οθόνης</translation>
</message>
<message>
<source>Enable Separate Update Folder</source>
<translation>Enable Separate Update Folder</translation>
</message>
<message>
<source>Default tab when opening settings</source>
<translation>Προεπιλεγμένη καρτέλα κατά την ανοίγμα των ρυθμίσεων</translation>
</message>
<message>
<source>Show Game Size In List</source>
<translation>Εμφάνιση Μεγέθους Παιχνιδιού στη Λίστα</translation>
@ -616,6 +628,14 @@
<source>Graphics</source>
<translation>Graphics</translation>
</message>
<message>
<source>Gui</source>
<translation>Διεπαφή</translation>
</message>
<message>
<source>User</source>
<translation>Χρήστης</translation>
</message>
<message>
<source>Graphics Device</source>
<translation>Graphics Device</translation>
@ -680,6 +700,26 @@
<source>Enable RenderDoc Debugging</source>
<translation>Enable RenderDoc Debugging</translation>
</message>
<message>
<source>Enable Crash Diagnostics</source>
<translation>Enable Crash Diagnostics</translation>
</message>
<message>
<source>Collect Shaders</source>
<translation>Collect Shaders</translation>
</message>
<message>
<source>Copy GPU Buffers</source>
<translation>Copy GPU Buffers</translation>
</message>
<message>
<source>Host Debug Markers</source>
<translation>Host Debug Markers</translation>
</message>
<message>
<source>Guest Debug Markers</source>
<translation>Guest Debug Markers</translation>
</message>
<message>
<source>Update</source>
<translation>Ενημέρωση</translation>
@ -700,6 +740,10 @@
<source>GUI Settings</source>
<translation>Ρυθμίσεις GUI</translation>
</message>
<message>
<source>Title Music</source>
<translation>Title Music</translation>
</message>
<message>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
@ -801,7 +845,7 @@
<translation>Ενημερώσεις:\nRelease: Επίσημες εκδόσεις που κυκλοφορούν μηνιαίως, είναι παλαιότερες αλλά πιο σταθερές και δοκιμασμένες.\nNightly: Εκδόσεις προγραμματιστών με νέες δυνατότητες και διορθώσεις, αλλά μπορεί να περιέχουν σφάλματα και να είναι λιγότερο σταθερές.</translation>
</message>
<message>
<source>GUIgroupBox</source>
<source>GUIMusicGroupBox</source>
<translation>Αναπαραγωγή Μουσικής Τίτλων:\nΕάν το παιχνίδι το υποστηρίζει, ενεργοποιεί ειδική μουσική κατά την επιλογή του παιχνιδιού από τη διεπαφή χρήστη.</translation>
</message>
<message>
@ -908,6 +952,26 @@
<source>rdocCheckBox</source>
<translation>Ενεργοποίηση Καταγραφής RenderDoc:\nΌταν είναι ενεργοποιημένο, ο εξομοιωτής είναι συμβατός με το RenderDoc για τη λήψη και ανάλυση του τρέχοντος καρέ.</translation>
</message>
<message>
<source>collectShaderCheckBox</source>
<translation>Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10).</translation>
</message>
<message>
<source>crashDiagnosticsCheckBox</source>
<translation>Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work.</translation>
</message>
<message>
<source>copyGPUBuffersCheckBox</source>
<translation>Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes.</translation>
</message>
<message>
<source>hostMarkersCheckBox</source>
<translation>Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</translation>
</message>
<message>
<source>guestMarkersCheckBox</source>
<translation>Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</translation>
</message>
</context>
<context>
<name>CheatsPatches</name>

View File

@ -247,6 +247,10 @@
<source>Recent Games</source>
<translation>Recent Games</translation>
</message>
<message>
<source>Open shadPS4 Folder</source>
<translation>Open shadPS4 Folder</translation>
</message>
<message>
<source>Exit</source>
<translation>Exit</translation>
@ -536,10 +540,18 @@
<source>Enable Fullscreen</source>
<translation>Enable Fullscreen</translation>
</message>
<message>
<source>Fullscreen Mode</source>
<translation>Fullscreen Mode</translation>
</message>
<message>
<source>Enable Separate Update Folder</source>
<translation>Enable Separate Update Folder</translation>
</message>
<message>
<source>Default tab when opening settings</source>
<translation>Default tab when opening settings</translation>
</message>
<message>
<source>Show Game Size In List</source>
<translation>Show Game Size In List</translation>
@ -616,6 +628,14 @@
<source>Graphics</source>
<translation>Graphics</translation>
</message>
<message>
<source>Gui</source>
<translation>Gui</translation>
</message>
<message>
<source>User</source>
<translation>User</translation>
</message>
<message>
<source>Graphics Device</source>
<translation>Graphics Device</translation>
@ -680,6 +700,27 @@
<source>Enable RenderDoc Debugging</source>
<translation>Enable RenderDoc Debugging</translation>
</message>
<message>
<source>Enable Crash Diagnostics</source>
<translation>Enable Crash Diagnostics</translation>
</message>
<message>
<source>Collect Shaders</source>
<translation>Collect Shaders</translation>
</message>
<message>
<source>Copy GPU Buffers</source>
<translation>Copy GPU Buffers</translation>
</message>
<message>
<source>Host Debug Markers</source>
<translation>Host Debug Markers</translation>
</message>
<message>
<source>Guest Debug Markers</source>
<translation>Guest Debug Markers</translation>
</message>
<message>
<source>Update</source>
<translation>Update</translation>
@ -700,6 +741,10 @@
<source>GUI Settings</source>
<translation>GUI Settings</translation>
</message>
<message>
<source>Title Music</source>
<translation>Title Music</translation>
</message>
<message>
<source>Disable Trophy Pop-ups</source>
<translation>Disable Trophy Pop-ups</translation>
@ -801,7 +846,7 @@
<translation>Update:\nRelease: Official versions released every month that may be very outdated, but are more reliable and tested.\nNightly: Development versions that have all the latest features and fixes, but may contain bugs and are less stable.</translation>
</message>
<message>
<source>GUIgroupBox</source>
<source>GUIMusicGroupBox</source>
<translation>Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI.</translation>
</message>
<message>
@ -908,6 +953,34 @@
<source>rdocCheckBox</source>
<translation>Enable RenderDoc Debugging:\nIf enabled, the emulator will provide compatibility with Renderdoc to allow capture and analysis of the currently rendered frame.</translation>
</message>
<message>
<source>collectShaderCheckBox</source>
<translation>Collect Shaders:\nYou need this enabled to edit shaders with the debug menu (Ctrl + F10).</translation>
</message>
<message>
<source>crashDiagnosticsCheckBox</source>
<translation>Crash Diagnostics:\nCreates a .yaml file with info about the Vulkan state at the time of crashing.\nUseful for debugging 'Device lost' errors. If you have this enabled, you should enable Host AND Guest Debug Markers.\nDoes not work on Intel GPUs.\nYou need Vulkan Validation Layers enabled and the Vulkan SDK for this to work.</translation>
</message>
<message>
<source>copyGPUBuffersCheckBox</source>
<translation>Copy GPU Buffers:\nGets around race conditions involving GPU submits.\nMay or may not help with PM4 type 0 crashes.</translation>
</message>
<message>
<source>hostMarkersCheckBox</source>
<translation>Host Debug Markers:\nInserts emulator-side information like markers for specific AMDGPU commands around Vulkan commands, as well as giving resources debug names.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</translation>
</message>
<message>
<source>guestMarkersCheckBox</source>
<translation>Guest Debug Markers:\nInserts any debug markers the game itself has added to the command buffer.\nIf you have this enabled, you should enable Crash Diagnostics.\nUseful for programs like RenderDoc.</translation>
</message>
<message>
<source>saveDataBox</source>
<translation>Save Data Path:\nThe folder where game save data will be saved.</translation>
</message>
<message>
<source>browseButton</source>
<translation>Browse:\nBrowse for a folder to set as the save data path.</translation>
</message>
</context>
<context>
<name>CheatsPatches</name>

Some files were not shown because too many files have changed in this diff Show More