Merge branch 'main' into bb-hacks

This commit is contained in:
sahelantropous 2024-08-22 05:36:06 +05:00
commit c4a4b45fb0
27 changed files with 901 additions and 123 deletions

View file

@ -193,6 +193,7 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
src/core/libraries/app_content/app_content.h
src/core/libraries/rtc/rtc.cpp
src/core/libraries/rtc/rtc.h
src/core/libraries/rtc/rtc_error.h
src/core/libraries/disc_map/disc_map.cpp
src/core/libraries/disc_map/disc_map.h
src/core/libraries/disc_map/disc_map_codes.h
@ -208,6 +209,11 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
src/core/libraries/avplayer/avplayer_state.h
src/core/libraries/avplayer/avplayer.cpp
src/core/libraries/avplayer/avplayer.h
src/core/libraries/ngs2/ngs2.cpp
src/core/libraries/ngs2/ngs2.h
src/core/libraries/ngs2/ngs2_error.h
src/core/libraries/ngs2/ngs2_impl.cpp
src/core/libraries/ngs2/ngs2_impl.h
)
set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h
@ -643,23 +649,40 @@ if (ENABLE_QT_GUI)
endif()
if (WIN32)
target_link_libraries(shadps4 PRIVATE mincore winpthreads clang_rt.builtins-x86_64.lib)
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS)
target_link_libraries(shadps4 PRIVATE mincore winpthreads)
if (MSVC)
# MSVC likes putting opinions on what people can use, disable:
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS)
endif()
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
if (MSVC)
# Needed for conflicts with time.h of windows.h
add_definitions(-D_TIMESPEC_DEFINED)
endif()
# Target Windows 10 RS5
add_definitions(-DNTDDI_VERSION=0x0A000006 -D_WIN32_WINNT=0x0A00 -DWINVER=0x0A00)
# Increase stack commit area
target_link_options(shadps4 PRIVATE /STACK:0x200000,0x200000)
if (MSVC)
target_link_libraries(shadps4 PRIVATE clang_rt.builtins-x86_64.lib)
endif()
# Disable ASLR so we can reserve the user area
if (MSVC)
target_link_options(shadps4 PRIVATE /DYNAMICBASE:NO)
else()
target_link_options(shadps4 PRIVATE -Wl,--disable-dynamicbase)
endif()
# Increase stack commit area (Needed, otherwise there are crashes)
if (MSVC)
target_link_options(shadps4 PRIVATE /STACK:0x200000,0x200000)
else()
target_link_options(shadps4 PRIVATE -Wl,--stack,2097152)
endif()
endif()
if (WIN32)

View file

@ -91,6 +91,10 @@ Check the build instructions for [**Linux**](https://github.com/shadps4-emu/shad
|macOS Qt Build|[![macOS-qt](https://github.com/shadps4-emu/shadPS4/actions/workflows/macos-qt.yml/badge.svg)](https://github.com/shadps4-emu/shadPS4/actions/workflows/macos-qt.yml)
</details>
# Debugging and reporting issues
For more information on how to test, debug and report issues with the emulator or games, read the [Debugging documentation](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md).
# Keyboard Mapping
| Controller button | Keyboard |

View file

@ -0,0 +1,156 @@
<!--
SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
SPDX-License-Identifier: GPL-2.0-or-later
-->
# Debugging and reporting issues about shadPS4 and games
This document covers information about debugging, troubleshooting and reporting developer-side issues related to shadPS4 and games.
## Setup
This section will guide you through setting up tools for debugging the emulator. This list will likely expand as more tools and platforms receive consistent setups.
<details>
<summary>Windows and Visual Studio</summary>
Make sure you have the project set up for building on Windows with Visual Studio and CMake: [Build shadPS4 for Windows
](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-windows.md)
1. Open the project folder in Visual Studio **as a folder**. _Do not run `cmake ..` or other commands that set up the project._
2. In the Solution Explorer, click the **Switch between solutions and available views** button.\
![image](https://github.com/user-attachments/assets/4e2be2b1-ba5a-4451-9ab2-f4ecf246213d)
3. Double-click on **CMake Targets View**.\
![image](https://github.com/user-attachments/assets/5ce7cf90-cd61-4cfa-bef5-645909827290)
4. Under **shadPS4 Project**, right-click on the **shadps4 (executable)** solution and click **Set as Startup Item**. This will let you start and debug shadPS4 using the VS debug buttons, as well as the default F5 shortcut.\
![image](https://github.com/user-attachments/assets/34c7c047-28a3-499f-be8f-df781134d104)
5. Right-click the **shadps4 (executable)** solution once more and click **Add debug configuration**.
6. Add an `"args: []"` section into the first `configurations` entry.\
List your game path as an argument, as if you were launching the non-GUI emulator from the command line.
![image](https://github.com/user-attachments/assets/8c7c3e69-f38f-4d6b-bdfd-4f1c41c50be7)
7. Set the appropriate CMake configuration for debugging or testing.
- For debugging the emulator and games within it, select `x64-Clang-Debug`.
- For testing the emulator with compiler optimizations as a release build, it is recommended to select `x64-Clang-RelWithDebInfo`,
as debug symbols will still be generated in case you encounter release configuration-exclusive bugs/errors.
![image](https://github.com/user-attachments/assets/0d975f7a-7bea-4f89-87ef-5d685bea4381)
Launch and debug the emulator through **Debug > Start Debugging** (F5 by default), or **Debug > Start Without Debugging** (Ctrl+F5 by default) when testing games for performance.
</details>
## Configuration
You can configure the emulator by editing the `config.toml` file found in the `user` folder created after starting the application.
<details>
<summary>Some configuration entries worth changing</summary>
- `[General]`
- `logType`: Configures logging synchronization (`sync`/`async`)
- By default, the emulator logs messages asynchronously for better performance. Some log messages may end up being received out-of-order.
- It can be beneficial to set this to `sync` in order for the log to accurately maintain message order, at the cost of performance.
- When communicating about issues with games and the log messages aren't clear due to potentially confusing order, set this to `sync` and send that log as well.
- `logFilter`: Sets the logging category for various logging classes.
- Format: `<class>:<level> ...`
- Multiple classes can be set by separating them with a space. (example: `Render:Warning Debug:Critical Lib.Pad:Error`)
- Sub-classes can be specified in the same format as seen in the console/log (such as `Core.Linker`).
- All classes and sub-classes can be set by specifying a `*` symbol. (example: `Kernel.*:Critical`)
- Valid log levels: `Trace, Debug, Info, Warning, Error, Critical` - in this order, setting a level silences all levels preceding it and logs every level after it.
- Examples:
- If the log is being spammed with messages coming from Lib.Pad, you can use `Lib.Pad:Critical` to only log critical-level messages.
- If you'd like to mute everything, but still want to receive messages from Vulkan rendering: `*:Critical Render.Vulkan:Info`
- `Fullscreen`: Display the game in a full screen borderless window.
- `[GPU]`
- `dumpShaders`: Dump shaders that are loaded by the emulator. Dump path: `../user/shader/dumps`
- `nullGpu`: Disables rendering.
- `screenWidth` and `screenHeight`: Configures the game window width and height.
- `[Vulkan]`
- `validation`-related settings: Use when debugging Vulkan.
- `rdocEnable`: Automatically hook RenderDoc when installed. Useful for debugging shaders and game rendering.
- `rdocMarkersEnable`: Enable automatic RenderDoc event annotation
- `[LLE]`
- `libc`: Use LLE with `libc`.
</details>
## Quick analysis
This section will provide some preliminary steps to take and tips on what to do when you encounter scenarios that require debugging.
<details open>
<summary>When a game crashes and breaks in the debugger</summary>
1. Analyze the log
- A console will open by default when you launch the emulator. It shows the same log messages that go into the log file found at `<emulator executable>/user/log/shad_log.txt`.
- It is recommended that you start analyzing the log bottom-up first:
- Are there any critical or error-level messages at the end of the log that would point to a reason for the game crashing?
- Do any of the last few messages contain information about the game loading files?
- Did the game window draw anything on-screen?
- Continue analyzing the log from the start to see other errors (such as with initialization, memory mapping, linker errors etc.)
2. Analyze the stack trace
- When the emulator is launched through a debugger, it will **break** when an exception or violation is encountered.\
_(**breaking** in this context means pausing execution of the program before it continues or stops altogether.
Breaks can be intentional as well - these are set with various kinds of **breakpoints**.)_
- Default setups of most debuggers include a **Stack trace** window/panel that lists the functions the program has called before breaking.
- The stack trace entries can be navigated to and will show the relevant function, as well as switch to the state that the program was in at the time of execution.\
Use the **Locals** and **Watch** windows to investigate variables and other code in these contexts.
3. Identify the reason for the crash
- **Logs aren't always accurate in determining the reason for a crash.**\
Some log entries are reported as errors but may not be fatal for the execution to stop. `Critical` entries are most likely to be the cause for crashes.
- Pinpoint the area of the emulator where the crash occured\
If the stack trace ends with functions that are relevant to rendering, it is safe to assume that the issue is with **rendering**.\
Similarly, if a crash is in a library responsible for playing videos, your issue can be narrowed down to the scope of video playback in the emulator.
- **⚠ Some crashes are intentional**
- If you identify **Access violations for writing operations** where the function is (or in cases of game libraries, _looks like_ it is) copying memory,
it most likely is an **intentional exception** meant to catch game data being written by the game.
This is used by the emulator developers to identify procedures that have to do with game data changing.
- Debugging tools usually include an option to not break on certain types of exceptions. **Exclude access violations and other intentional exceptions when debugging to skip these exceptions.**
- You can also identify such cases if the game works in Release builds of the emulator. These intentional exceptions are development-time only.
- Attempt to **Continue** and observe whether the stack trace and/or variables and registers change when you encounter exceptions.
</details>
## Reporting and communicating about issues
When communicating with the project about game-specific issues, specify an **uniquely identifable game name** along with its `CUSA-xxxxx` code that is specific to the region/variant of the game you're testing.\
The version number is also important to add at least in the description, especially if you can verify that the game behaves differently across versions.\
Accurately identifying games will help other developers that own that game recognize your issue by its title and jump in to help test and debug it.
- Examples of good naming schemes:
- Amplitude (2016) `CUSA02480`
- Rock Band 4 (`CUSA02084`) v1.0
- inFamous: Second Son \[`CUSA-00004`\]
- Examples of unideal naming schemes:
- _The Witness_
- _GTA 5_
- _Watch Dogs_
- If your issue is small or you aren't sure whether you have properly identified something, [join the Discord server](https://discord.gg/MyZRaBngxA) and use the #development channel
to concisely explain the issue, as well as any findings you currently have.
- It is recommended that you check the [game compatibility issue tracker](https://github.com/shadps4-emu/shadps4-game-compatibility/issues) and post very short summaries of progress changes there,
(such as the game now booting into the menu or getting in-game) for organizational and status update purposes.
- ⚠ **Do not post theoretical, unproven game-specific issues in the emulator issue tracker that you cannot verify and locate in the emulator source code as being a bug.**\
Do, however, add information about the game you experienced the issue in, so that it can be tested in a reproducible environment.
- Good example: "_Crash in `Shader::Gcn::CFG::EmitBlocks()`, out of bounds list access_" -> _issue description shares stack trace, points to code in the repository and provides relevant information_
- Bad example: "_Amplitude crashes on boot, access violation_" -> _issue description reiterates title, focuses on the game instead of the emulator and refuses to elaborate_

View file

@ -58,4 +58,24 @@ To install PKG files (game and updates), you will need the Qt application (with
## Configure the emulator
You can configure the emulator in the "user" folder (created after the first start of the application) then in the "config.toml" file. Here you can find lots of parameters to set with True or False.
You can configure the emulator by editing the `config.toml` file found in the `user` folder created after starting the application.\
Some settings may be related to more technical development and debugging. For more information on those, see [Debugging](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#configuration).
Here's a list of configuration entries that are worth changing:
- `[General]`
- `Fullscreen`: Display the game in a full screen borderless window.
- `logType`: Configures logging synchronization (`sync`/`async`)
- It can be beneficial to set this to `sync` in order for the log to accurately maintain message order, at the cost of performance.
- Use when sending logs to developers. See more about [reporting issues](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md#reporting-and-communicating-about-issues).
- `logFilter`: Sets the logging category for various logging classes.
- Format: `<class>:<level> ...`, `<class.*>:<level> <*:level> ...`
- Valid log levels: `Trace, Debug, Info, Warning, Error, Critical` - in this order, setting a level silences all levels preceding it and logs every level after it.
- Examples:
- If the log is being spammed with messages coming from Lib.Pad, you can use `Lib.Pad:Critical` to only log critical-level messages.
- If you'd like to mute everything, but still want to receive messages from Vulkan rendering: `*:Error Render.Vulkan:Info`
- `[GPU]`
- `screenWidth` and `screenHeight`: Configures the game window width and height.

View file

@ -5,21 +5,77 @@ SPDX-License-Identifier: GPL-2.0-or-later
# Build shadPS4 for Windows
## Download Visual Studio Community 2022
This tutorial reads as if you have none of the prerequisites already installed. If you do, just ignore the steps regarding installation.
If you are building to contribute to the project, please omit `--depth 1` from the git invokations.
Download link: [**Visual Studio 2022**](https://visualstudio.microsoft.com/vs/)
Note: **ARM64 is not supported!** As of writing, it will not build nor run. The instructions with respect to ARM64 are for developers only.
## Requirements
## Option 1: Visual Studio 2022
### From Visual Studio Community
### (Prerequisite) Download the Community edition from [**Visual Studio 2022**](https://visualstudio.microsoft.com/vs/)
- Desktop development with C++
Once you are within the installer:
1. Select `Desktop development with C++`
2. Go to "Individual Components" tab
3. Make sure `C++ Clang Compiler for Windows`, `MSBuild support for LLVM` and `C++ CMake Tools for Windows` are selected
4. Continue the installation
### From individual components tab install
### (Prerequisite) Download [**Qt**](https://doc.qt.io/qt-6/get-and-install-qt.html)
- C++ Clang Compiler for Windows (17.0.3)
- MSBuild support for LLVM (Clang-cl) toolset
Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead.
- ## Compiling
1. Select Qt for Visual Studio plugin
2. Select `msvc2019_64` option or similar. If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `msvc2019_arm64`
- Open Visual Studio Community and select the **x64-Clang-Release**, **x64-Clang-Debug** or **x64-Clang-RelWithDebInfo**. It should compile just fine.
Go through the installation normally. If you do not know what components to select, just select the newest Qt version it gives you.
If you know what you are doing, you may unselect individual components that eat up too much disk space.
Once you are finished, you will have to configure Qt within Visual Studio:
1. Tools -> Options -> Qt -> Versions
2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.1\msvc2019_64`
3. Enable the default checkmark on the new version you just created.
### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win)
Go through the Git for Windows installation as normal
### Compiling with Visual Studio GUI
1. Open Git for Windows, navigate to a place where you want to store the shadPS4 source code folder
2. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
3. Open up Visual Studio, select `Open a local folder` and select the folder with the shadPS4 source code. The folder should contain `CMakeLists.txt`
4. Build -> Build All
## Option 2: MSYS2/MinGW
### (Prerequisite) Download [**MSYS2**](https://www.msys2.org/)
Go through the MSYS2 installation as normal
If you are building to distribute, please omit `-DCMAKE_CXX_FLAGS="-O2 -march=native"` within the build configuration step.
Normal x86-based computers, follow:
1. Open "MSYS2 MINGW64" from your new applications
2. Run `pacman -Syu`, let it complete;
3. Run `pacman -S --needed git mingw-w64-x86_64-binutils mingw-w64-x86_64-clang mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-qt6-base`
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
5. Run `cd shadPS4`
6. Run `cmake -S . -B build -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"`
7. Run `cmake --build build`
8. To run the finished product, run `./build/shadPS4.exe`
ARM64-based computers, follow:
1. Open "MSYS2 CLANGARM64" from your new applications
2. Run `pacman -Syu`, let it complete;
3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-qt6-base`
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
5. Run `cd shadPS4`
6. Run `cmake -S . -B build -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"`
7. Run `cmake --build build`
8. To run the finished product, run `./build/shadPS4.exe`
## Note on MSYS2 builds
These builds may not be easily copyable to people who do not also have a MSYS2 installation.
If you want to distribute these builds, you need to copy over the correct DLLs into a distribution folder.
In order to run them, you must be within the MSYS2 shell environment.

View file

@ -8,6 +8,7 @@
#ifdef __APPLE__
#include <CoreFoundation/CFBundle.h>
#include <dlfcn.h>
#include <sys/param.h>
#endif
@ -26,23 +27,52 @@ namespace Common::FS {
namespace fs = std::filesystem;
#ifdef __APPLE__
using IsTranslocatedURLFunc = Boolean (*)(CFURLRef path, bool* isTranslocated,
CFErrorRef* __nullable error);
using CreateOriginalPathForURLFunc = CFURLRef __nullable (*)(CFURLRef translocatedPath,
CFErrorRef* __nullable error);
static CFURLRef UntranslocateBundlePath(const CFURLRef bundle_path) {
if (void* security_handle =
dlopen("/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY)) {
SCOPE_EXIT {
dlclose(security_handle);
};
const auto IsTranslocatedURL = reinterpret_cast<IsTranslocatedURLFunc>(
dlsym(security_handle, "SecTranslocateIsTranslocatedURL"));
const auto CreateOriginalPathForURL = reinterpret_cast<CreateOriginalPathForURLFunc>(
dlsym(security_handle, "SecTranslocateCreateOriginalPathForURL"));
bool is_translocated = false;
if (IsTranslocatedURL && CreateOriginalPathForURL &&
IsTranslocatedURL(bundle_path, &is_translocated, nullptr) && is_translocated) {
return CreateOriginalPathForURL(bundle_path, nullptr);
}
}
return nullptr;
}
static std::filesystem::path GetBundleParentDirectory() {
if (CFBundleRef bundle_ref = CFBundleGetMainBundle()) {
if (CFURLRef bundle_url_ref = CFBundleCopyBundleURL(bundle_ref)) {
SCOPE_EXIT {
CFRelease(bundle_url_ref);
};
if (CFStringRef bundle_path_ref =
CFURLCopyFileSystemPath(bundle_url_ref, kCFURLPOSIXPathStyle)) {
SCOPE_EXIT {
CFRelease(bundle_path_ref);
};
char app_bundle_path[MAXPATHLEN];
if (CFStringGetFileSystemRepresentation(bundle_path_ref, app_bundle_path,
sizeof(app_bundle_path))) {
std::filesystem::path bundle_path{app_bundle_path};
return bundle_path.parent_path();
CFURLRef untranslocated_url_ref = UntranslocateBundlePath(bundle_url_ref);
SCOPE_EXIT {
if (untranslocated_url_ref) {
CFRelease(untranslocated_url_ref);
}
};
char app_bundle_path[MAXPATHLEN];
if (CFURLGetFileSystemRepresentation(
untranslocated_url_ref ? untranslocated_url_ref : bundle_url_ref, true,
reinterpret_cast<u8*>(app_bundle_path), sizeof(app_bundle_path))) {
std::filesystem::path bundle_path{app_bundle_path};
return bundle_path.parent_path();
}
}
}

View file

@ -1,16 +1,75 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/io_file.h"
#include "playgo_chunk.h"
bool PlaygoChunk::Open(const std::filesystem::path& filepath) {
bool PlaygoFile::Open(const std::filesystem::path& filepath) {
Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read);
if (!file.IsOpen()) {
return false;
if (file.IsOpen()) {
file.Read(playgoHeader);
if (LoadChunks(file)) {
return true;
}
}
file.Read(playgoHeader);
return false;
}
return true;
bool PlaygoFile::LoadChunks(const Common::FS::IOFile& file) {
if (file.IsOpen()) {
if (playgoHeader.magic == PLAYGO_MAGIC) {
bool ret = true;
std::string chunk_attrs_data, chunk_mchunks_data, chunk_labels_data, mchunk_attrs_data;
ret = ret && load_chunk_data(file, playgoHeader.chunk_attrs, chunk_attrs_data);
ret = ret && load_chunk_data(file, playgoHeader.chunk_mchunks, chunk_mchunks_data);
ret = ret && load_chunk_data(file, playgoHeader.chunk_labels, chunk_labels_data);
ret = ret && load_chunk_data(file, playgoHeader.mchunk_attrs, mchunk_attrs_data);
if (ret) {
chunks.resize(playgoHeader.chunk_count);
auto chunk_attrs =
reinterpret_cast<playgo_chunk_attr_entry_t*>(&chunk_attrs_data[0]);
auto chunk_mchunks = reinterpret_cast<u16*>(&chunk_mchunks_data[0]);
auto chunk_labels = reinterpret_cast<char*>(&chunk_labels_data[0]);
auto mchunk_attrs =
reinterpret_cast<playgo_mchunk_attr_entry_t*>(&mchunk_attrs_data[0]);
for (u16 i = 0; i < playgoHeader.chunk_count; i++) {
chunks[i].req_locus = chunk_attrs[i].req_locus;
chunks[i].language_mask = chunk_attrs[i].language_mask;
chunks[i].label_name = std::string(chunk_labels + chunk_attrs[i].label_offset);
u64 total_size = 0;
u16 mchunk_count = chunk_attrs[i].mchunk_count;
if (mchunk_count != 0) {
auto mchunks = reinterpret_cast<u16*>(
((u8*)chunk_mchunks + chunk_attrs[i].mchunks_offset));
for (u16 j = 0; j < mchunk_count; j++) {
u16 mchunk_id = mchunks[j];
total_size += mchunk_attrs[mchunk_id].size.size;
}
}
chunks[i].total_size = total_size;
}
}
return ret;
}
}
return false;
}
bool PlaygoFile::load_chunk_data(const Common::FS::IOFile& file, const chunk_t& chunk,
std::string& data) {
if (file.IsOpen()) {
if (file.Seek(chunk.offset)) {
data.resize(chunk.length);
if (data.size() == chunk.length) {
file.ReadRaw<char>(&data[0], chunk.length);
return true;
}
}
}
return false;
}

View file

@ -3,29 +3,129 @@
#pragma once
#include <filesystem>
#include "common/types.h"
#include <mutex>
#include <vector>
#include "common/io_file.h"
#include "core/libraries/playgo/playgo_types.h"
constexpr u32 PLAYGO_MAGIC = 0x6F676C70;
struct chunk_t {
u32 offset;
u32 length;
} __attribute__((packed));
struct PlaygoHeader {
u32 magic;
u16 version_major;
u16 version_minor;
u16 image_count;
u16 image_count; // [0;1]
u16 chunk_count; // [0;1000]
u16 mchunk_count; // [0;8000]
u16 scenario_count; // [0;32]
u32 file_size;
u16 default_scenario_id;
u16 attrib;
u32 sdk_version;
u16 disc_count; // [0;2] (if equals to 0 then disc count = 1)
u16 layer_bmp;
u8 reserved[32];
char content_id[128];
chunk_t chunk_attrs; // [0;32000]
chunk_t chunk_mchunks;
chunk_t chunk_labels; // [0;16000]
chunk_t mchunk_attrs; // [0;12800]
chunk_t scenario_attrs; // [0;1024]
chunk_t scenario_chunks;
chunk_t scenario_labels;
chunk_t inner_mchunk_attrs; // [0;12800]
} __attribute__((packed));
struct playgo_scenario_attr_entry_t {
u8 _type;
u8 _unk[19];
u16 initial_chunk_count;
u16 chunk_count;
u32 chunks_offset; //<-scenario_chunks
u32 label_offset; //<-scenario_labels
} __attribute__((packed));
struct image_disc_layer_no_t {
u8 layer_no : 2;
u8 disc_no : 2;
u8 image_no : 4;
} __attribute__((packed));
struct playgo_chunk_attr_entry_t {
u8 flag;
image_disc_layer_no_t image_disc_layer_no;
u8 req_locus;
u8 unk[11];
u16 mchunk_count;
u16 scenario_count;
// TODO fill the rest
u64 language_mask;
u32 mchunks_offset; //<-chunk_mchunks
u32 label_offset; //<-chunk_labels
} __attribute__((packed));
struct playgo_chunk_loc_t {
u64 offset : 48;
u64 _align1 : 8;
u64 image_no : 4;
u64 _align2 : 4;
} __attribute__((packed));
struct playgo_chunk_size_t {
u64 size : 48;
u64 _align : 16;
} __attribute__((packed));
struct playgo_mchunk_attr_entry_t {
playgo_chunk_loc_t loc;
playgo_chunk_size_t size;
} __attribute__((packed));
struct PlaygoChunk {
u64 req_locus;
u64 language_mask;
u64 total_size;
std::string label_name;
};
class PlaygoChunk {
class PlaygoFile {
public:
PlaygoChunk() = default;
~PlaygoChunk() = default;
bool initialized;
OrbisPlayGoHandle handle;
OrbisPlayGoChunkId id;
OrbisPlayGoLocus locus;
OrbisPlayGoInstallSpeed speed;
s64 speed_tick;
OrbisPlayGoEta eta;
OrbisPlayGoLanguageMask langMask;
std::vector<PlaygoChunk> chunks;
public:
PlaygoFile()
: initialized(false), handle(0), id(0), locus(0), speed(ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE),
speed_tick(0), eta(0), langMask(0), playgoHeader{0} {}
~PlaygoFile() = default;
bool Open(const std::filesystem::path& filepath);
PlaygoHeader GetPlaygoHeader() {
bool LoadChunks(const Common::FS::IOFile& file);
PlaygoHeader& GetPlaygoHeader() {
return playgoHeader;
}
std::mutex& GetSpeedMutex() {
return speed_mutex;
}
private:
bool load_chunk_data(const Common::FS::IOFile& file, const chunk_t& chunk, std::string& data);
private:
PlaygoHeader playgoHeader;
};
std::mutex speed_mutex;
};

View file

@ -359,12 +359,16 @@ bool AvPlayerSource::GetAudioData(SceAvPlayerFrameInfo& audio_info) {
audio_info = {};
audio_info.timestamp = frame->info.timestamp;
audio_info.pData = reinterpret_cast<u8*>(frame->info.pData);
audio_info.details.audio.sample_rate = frame->info.details.audio.sample_rate;
audio_info.details.audio.size = frame->info.details.audio.size;
audio_info.details.audio.channel_count = frame->info.details.audio.channel_count;
return true;
}
u64 AvPlayerSource::CurrentTime() {
if (!IsActive()) {
return 0;
}
using namespace std::chrono;
return duration_cast<milliseconds>(high_resolution_clock::now() - m_start_time).count();
}
@ -655,6 +659,7 @@ Frame AvPlayerSource::PrepareAudioFrame(FrameBuffer buffer, const AVFrame& frame
.audio =
{
.channel_count = u16(frame.ch_layout.nb_channels),
.sample_rate = u32(frame.sample_rate),
.size = u32(size),
},
},

View file

@ -12,22 +12,23 @@ using namespace Libraries::Kernel;
namespace Libraries::Ngs2 {
s32 Ngs2::ReportInvalid(u32 handle_type) const {
s32 Ngs2::ReportInvalid(Ngs2Handle* handle, u32 handle_type) const {
uintptr_t hAddress = reinterpret_cast<uintptr_t>(handle);
switch (handle_type) {
case 1:
LOG_ERROR(Lib_Ngs2, "Invalid system handle {}", this);
LOG_ERROR(Lib_Ngs2, "Invalid system handle {}", hAddress);
return ORBIS_NGS2_ERROR_INVALID_SYSTEM_HANDLE;
case 2:
LOG_ERROR(Lib_Ngs2, "Invalid rack handle {}", this);
LOG_ERROR(Lib_Ngs2, "Invalid rack handle {}", hAddress);
return ORBIS_NGS2_ERROR_INVALID_RACK_HANDLE;
case 4:
LOG_ERROR(Lib_Ngs2, "Invalid voice handle {}", this);
LOG_ERROR(Lib_Ngs2, "Invalid voice handle {}", hAddress);
return ORBIS_NGS2_ERROR_INVALID_VOICE_HANDLE;
case 8:
LOG_ERROR(Lib_Ngs2, "Invalid report handle {}", this);
LOG_ERROR(Lib_Ngs2, "Invalid report handle {}", hAddress);
return ORBIS_NGS2_ERROR_INVALID_REPORT_HANDLE;
default:
LOG_ERROR(Lib_Ngs2, "Invalid handle {}", this);
LOG_ERROR(Lib_Ngs2, "Invalid handle {}", hAddress);
return ORBIS_NGS2_ERROR_INVALID_HANDLE;
}
}
@ -58,24 +59,24 @@ s32 Ngs2::HandleCleanup(Ngs2Handle* handle, u32 hType, void* dataOut) {
}
}
}
return HandleReportInvalid(handle, hType);
return this->ReportInvalid(handle, hType);
}
s32 Ngs2::HandleEnter(Ngs2Handle* handle, u32 hType, Ngs2Handle* handleOut) {
if (!handle) {
return HandleReportInvalid(handle, 0);
return this->ReportInvalid(handle, 0);
}
if (handle->selfPointer != handle || !handle->atomicPtr || !handle->dataPointer ||
(~hType & handle->handleType)) {
return HandleReportInvalid(handle, handle->handleType);
return this->ReportInvalid(handle, handle->handleType);
}
std::atomic<u32>* atomic = handle->atomicPtr;
while (true) {
u32 i = atomic->load();
if (i == 0) {
return HandleReportInvalid(handle, handle->handleType);
return this->ReportInvalid(handle, handle->handleType);
}
if (atomic->compare_exchange_strong(i, i + 1)) {
break;
@ -83,7 +84,7 @@ s32 Ngs2::HandleEnter(Ngs2Handle* handle, u32 hType, Ngs2Handle* handleOut) {
}
if (handleOut) {
*handleOut = handle;
handleOut = handle;
}
return ORBIS_OK;
}

View file

@ -9,7 +9,7 @@ namespace Libraries::Ngs2 {
class Ngs2 {
public:
s32 ReportInvalid(u32 handle_type) const;
s32 ReportInvalid(Ngs2Handle* handle, u32 handle_type) const;
s32 HandleSetup(Ngs2Handle* handle, void* data, std::atomic<u32>* atomic, u32 type, u32 flags);
s32 HandleCleanup(Ngs2Handle* handle, u32 hType, void* dataOut);
s32 HandleEnter(Ngs2Handle* handle, u32 hType, Ngs2Handle* handleOut);

View file

@ -6,6 +6,7 @@
#include "common/singleton.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "core/libraries/system/systemservice.h"
#include "playgo.h"
namespace Libraries::PlayGo {
@ -20,42 +21,129 @@ s32 PS4_SYSV_ABI sceDbgPlayGoSnapshot() {
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePlayGoClose() {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called");
s32 PS4_SYSV_ABI scePlayGoClose(OrbisPlayGoHandle handle) {
LOG_INFO(Lib_PlayGo, "called");
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
if (handle != 1)
return ORBIS_PLAYGO_ERROR_BAD_HANDLE;
if (!playgo->initialized)
return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePlayGoGetChunkId() {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called");
s32 PS4_SYSV_ABI scePlayGoGetChunkId(OrbisPlayGoHandle handle, OrbisPlayGoChunkId* outChunkIdList,
u32 numberOfEntries, u32* outEntries) {
LOG_INFO(Lib_PlayGo, "called");
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
if (handle != 1)
return ORBIS_PLAYGO_ERROR_BAD_HANDLE;
if (outEntries == nullptr)
return ORBIS_PLAYGO_ERROR_BAD_POINTER;
if (outChunkIdList != nullptr && numberOfEntries == 0)
return ORBIS_PLAYGO_ERROR_BAD_SIZE;
if (playgo->GetPlaygoHeader().file_size == 0) {
*outEntries = 0;
} else {
if (outChunkIdList == nullptr) {
*outEntries = playgo->chunks.size();
} else {
if (numberOfEntries > playgo->chunks.size()) {
numberOfEntries = playgo->chunks.size();
}
if (numberOfEntries != 0) {
for (u32 i = 0; i < numberOfEntries; i++) {
outChunkIdList[i] = i;
}
*outEntries = numberOfEntries;
}
}
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePlayGoGetEta() {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called");
s32 PS4_SYSV_ABI scePlayGoGetEta(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds,
u32 numberOfEntries, OrbisPlayGoEta* outEta) {
LOG_INFO(Lib_PlayGo, "called");
if (handle != 1)
return ORBIS_PLAYGO_ERROR_BAD_HANDLE;
if (chunkIds == nullptr || outEta == nullptr)
return ORBIS_PLAYGO_ERROR_BAD_POINTER;
if (numberOfEntries == 0)
return ORBIS_PLAYGO_ERROR_BAD_SIZE;
*outEta = 0; // all is loaded
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePlayGoGetInstallSpeed() {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called");
s32 PS4_SYSV_ABI scePlayGoGetInstallSpeed(OrbisPlayGoHandle handle,
OrbisPlayGoInstallSpeed* outSpeed) {
LOG_INFO(Lib_PlayGo, "called");
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
if (handle != 1)
return ORBIS_PLAYGO_ERROR_BAD_HANDLE;
if (outSpeed == nullptr)
return ORBIS_PLAYGO_ERROR_BAD_POINTER;
if (!playgo->initialized)
return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED;
std::scoped_lock lk{playgo->GetSpeedMutex()};
if (playgo->speed == 0) {
using namespace std::chrono;
if ((duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count() -
playgo->speed_tick) > 30 * 1000) { // 30sec
playgo->speed = ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE;
}
}
*outSpeed = playgo->speed;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePlayGoGetLanguageMask(OrbisPlayGoHandle handle,
OrbisPlayGoLanguageMask* languageMask) {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called");
*languageMask = 1; // En, todo;
OrbisPlayGoLanguageMask* outLanguageMask) {
LOG_INFO(Lib_PlayGo, "called");
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
if (handle != 1)
return ORBIS_PLAYGO_ERROR_BAD_HANDLE;
if (outLanguageMask == nullptr)
return ORBIS_PLAYGO_ERROR_BAD_POINTER;
if (!playgo->initialized)
return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED;
*outLanguageMask = playgo->langMask;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds,
uint32_t numberOfEntries, OrbisPlayGoLocus* outLoci) {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called handle = {}, chunkIds = {}, numberOfEntries = {}",
handle, *chunkIds, numberOfEntries);
LOG_INFO(Lib_PlayGo, "called handle = {}, chunkIds = {}, numberOfEntries = {}", handle,
*chunkIds, numberOfEntries);
auto* playgo = Common::Singleton<PlaygoChunk>::Instance();
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
if (handle != 1)
return ORBIS_PLAYGO_ERROR_BAD_HANDLE;
if (chunkIds == nullptr || outLoci == nullptr)
return ORBIS_PLAYGO_ERROR_BAD_POINTER;
if (numberOfEntries == 0)
return ORBIS_PLAYGO_ERROR_BAD_SIZE;
if (!playgo->initialized)
return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED;
if (playgo->GetPlaygoHeader().file_size == 0)
return ORBIS_PLAYGO_ERROR_NOT_SUPPORT_PLAYGO;
for (uint32_t i = 0; i < numberOfEntries; i++) {
if (chunkIds[i] <= playgo->GetPlaygoHeader().mchunk_count) {
if (chunkIds[i] <= playgo->chunks.size()) {
outLoci[i] = OrbisPlayGoLocusValue::ORBIS_PLAYGO_LOCUS_LOCAL_FAST;
} else {
outLoci[i] = ORBIS_PLAYGO_LOCUS_NOT_DOWNLOADED;
@ -67,64 +155,202 @@ s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoCh
s32 PS4_SYSV_ABI scePlayGoGetProgress(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds,
uint32_t numberOfEntries, OrbisPlayGoProgress* outProgress) {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called handle = {}, chunkIds = {}, numberOfEntries = {}",
handle, *chunkIds, numberOfEntries);
outProgress->progressSize = 0x10000; // todo?
outProgress->totalSize = 0x10000;
LOG_INFO(Lib_PlayGo, "called handle = {}, chunkIds = {}, numberOfEntries = {}", handle,
*chunkIds, numberOfEntries);
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
if (handle != 1)
return ORBIS_PLAYGO_ERROR_BAD_HANDLE;
if (chunkIds == nullptr || outProgress == nullptr)
return ORBIS_PLAYGO_ERROR_BAD_POINTER;
if (numberOfEntries == 0)
return ORBIS_PLAYGO_ERROR_BAD_SIZE;
if (!playgo->initialized)
return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED;
if (playgo->GetPlaygoHeader().file_size == 0)
return ORBIS_PLAYGO_ERROR_BAD_CHUNK_ID;
outProgress->progressSize = 0;
outProgress->totalSize = 0;
u64 total_size = 0;
for (u32 i = 0; i < numberOfEntries; i++) {
u32 chunk_id = chunkIds[i];
if (chunk_id < playgo->chunks.size()) {
total_size += playgo->chunks[chunk_id].total_size;
} else {
return ORBIS_PLAYGO_ERROR_BAD_CHUNK_ID;
}
}
outProgress->progressSize = total_size;
outProgress->totalSize = total_size;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePlayGoGetToDoList(OrbisPlayGoHandle handle, OrbisPlayGoToDo* outTodoList,
u32 numberOfEntries, u32* outEntries) {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called handle = {} numberOfEntries = {}", handle,
numberOfEntries);
LOG_INFO(Lib_PlayGo, "called handle = {} numberOfEntries = {}", handle, numberOfEntries);
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
if (handle != 1)
return ORBIS_PLAYGO_ERROR_BAD_HANDLE;
if (outTodoList == nullptr)
if (outTodoList == nullptr || outEntries == nullptr)
return ORBIS_PLAYGO_ERROR_BAD_POINTER;
if (numberOfEntries == 0)
return ORBIS_PLAYGO_ERROR_BAD_SIZE;
if (!playgo->initialized)
return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED;
*outEntries = 0; // nothing to do
return ORBIS_OK;
}
int scePlayGoConvertLanguage(int systemLang) {
if (systemLang >= 0 && systemLang < 48) {
return (1 << (64 - systemLang - 1));
} else {
return 0;
}
}
s32 PS4_SYSV_ABI scePlayGoInitialize(OrbisPlayGoInitParams* param) {
LOG_INFO(Lib_PlayGo, "called, bufSize = {}", param->bufSize);
if (param->bufAddr == nullptr)
return ORBIS_PLAYGO_ERROR_BAD_POINTER;
if (param->bufSize < 0x200000)
return ORBIS_PLAYGO_ERROR_BAD_SIZE;
LOG_INFO(Lib_PlayGo, "(STUBBED)called, bufSize = {}", param->bufSize);
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
if (!playgo->initialized) {
using namespace SystemService;
// get system lang
int systemLang = 0;
sceSystemServiceParamGetInt(ORBIS_SYSTEM_SERVICE_PARAM_ID_LANG, &systemLang);
playgo->langMask = scePlayGoConvertLanguage(systemLang);
playgo->initialized = true;
} else {
return ORBIS_PLAYGO_ERROR_ALREADY_INITIALIZED;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePlayGoOpen(OrbisPlayGoHandle* outHandle, const void* param) {
*outHandle = 1;
LOG_INFO(Lib_PlayGo, "(STUBBED)called");
LOG_INFO(Lib_PlayGo, "called");
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
if (outHandle == nullptr)
return ORBIS_PLAYGO_ERROR_BAD_POINTER;
if (param)
return ORBIS_PLAYGO_ERROR_INVALID_ARGUMENT;
if (!playgo->initialized)
return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED;
if (playgo->GetPlaygoHeader().file_size == 0)
return ORBIS_PLAYGO_ERROR_NOT_SUPPORT_PLAYGO;
playgo->handle = *outHandle = 1;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePlayGoPrefetch() {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called");
s32 PS4_SYSV_ABI scePlayGoPrefetch(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds,
u32 numberOfEntries, OrbisPlayGoLocus minimumLocus) {
LOG_INFO(Lib_PlayGo, "called");
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
if (handle != 1)
return ORBIS_PLAYGO_ERROR_BAD_HANDLE;
if (chunkIds == nullptr)
return ORBIS_PLAYGO_ERROR_BAD_POINTER;
if (numberOfEntries == 0)
return ORBIS_PLAYGO_ERROR_BAD_SIZE;
if (!playgo->initialized)
return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED;
switch (minimumLocus) {
case ORBIS_PLAYGO_LOCUS_NOT_DOWNLOADED:
case ORBIS_PLAYGO_LOCUS_LOCAL_SLOW:
case ORBIS_PLAYGO_LOCUS_LOCAL_FAST:
break;
default:
return ORBIS_PLAYGO_ERROR_BAD_LOCUS;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePlayGoSetInstallSpeed() {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called");
s32 PS4_SYSV_ABI scePlayGoSetInstallSpeed(OrbisPlayGoHandle handle, OrbisPlayGoInstallSpeed speed) {
LOG_INFO(Lib_PlayGo, "called");
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
if (handle != 1)
return ORBIS_PLAYGO_ERROR_BAD_HANDLE;
if (!playgo->initialized)
return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED;
switch (speed) {
case ORBIS_PLAYGO_INSTALL_SPEED_SUSPENDED:
case ORBIS_PLAYGO_INSTALL_SPEED_TRICKLE:
case ORBIS_PLAYGO_INSTALL_SPEED_FULL:
break;
default:
return ORBIS_PLAYGO_ERROR_INVALID_ARGUMENT;
}
std::scoped_lock lk{playgo->GetSpeedMutex()};
using namespace std::chrono;
playgo->speed = speed;
playgo->speed_tick =
duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePlayGoSetLanguageMask(OrbisPlayGoHandle handle,
OrbisPlayGoLanguageMask languageMask) {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called");
LOG_INFO(Lib_PlayGo, "called");
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
if (handle != 1)
return ORBIS_PLAYGO_ERROR_BAD_HANDLE;
if (!playgo->initialized)
return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED;
playgo->langMask = languageMask;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePlayGoSetToDoList(OrbisPlayGoHandle handle, const OrbisPlayGoToDo* todoList,
uint32_t numberOfEntries) {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called");
LOG_INFO(Lib_PlayGo, "called");
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
if (handle != 1)
return ORBIS_PLAYGO_ERROR_BAD_HANDLE;
if (todoList == nullptr)
return ORBIS_PLAYGO_ERROR_BAD_POINTER;
if (numberOfEntries == 0)
return ORBIS_PLAYGO_ERROR_BAD_SIZE;
if (!playgo->initialized)
return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI scePlayGoTerminate() {
LOG_ERROR(Lib_PlayGo, "(STUBBED)called");
LOG_INFO(Lib_PlayGo, "called");
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
if (playgo->initialized) {
playgo->initialized = false;
} else {
return ORBIS_PLAYGO_ERROR_NOT_INITIALIZED;
}
return ORBIS_OK;
}

View file

@ -14,10 +14,13 @@ constexpr int shadMagic = 0x53484144;
s32 PS4_SYSV_ABI sceDbgPlayGoRequestNextChunk();
s32 PS4_SYSV_ABI sceDbgPlayGoSnapshot();
s32 PS4_SYSV_ABI scePlayGoClose();
s32 PS4_SYSV_ABI scePlayGoGetChunkId();
s32 PS4_SYSV_ABI scePlayGoGetEta();
s32 PS4_SYSV_ABI scePlayGoGetInstallSpeed();
s32 PS4_SYSV_ABI scePlayGoClose(OrbisPlayGoHandle handle);
s32 PS4_SYSV_ABI scePlayGoGetChunkId(OrbisPlayGoHandle handle, OrbisPlayGoChunkId* outChunkIdList,
u32 numberOfEntries, u32* outEntries);
s32 PS4_SYSV_ABI scePlayGoGetEta(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds,
u32 numberOfEntries, OrbisPlayGoEta* outEta);
s32 PS4_SYSV_ABI scePlayGoGetInstallSpeed(OrbisPlayGoHandle handle,
OrbisPlayGoInstallSpeed* outSpeed);
s32 PS4_SYSV_ABI scePlayGoGetLanguageMask(OrbisPlayGoHandle handle,
OrbisPlayGoLanguageMask* outLanguageMask);
s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds,
@ -28,8 +31,9 @@ s32 PS4_SYSV_ABI scePlayGoGetToDoList(OrbisPlayGoHandle handle, OrbisPlayGoToDo*
u32 numberOfEntries, u32* outEntries);
s32 PS4_SYSV_ABI scePlayGoInitialize(OrbisPlayGoInitParams* param);
s32 PS4_SYSV_ABI scePlayGoOpen(OrbisPlayGoHandle* outHandle, const void* param);
s32 PS4_SYSV_ABI scePlayGoPrefetch();
s32 PS4_SYSV_ABI scePlayGoSetInstallSpeed();
s32 PS4_SYSV_ABI scePlayGoPrefetch(OrbisPlayGoHandle handle, const OrbisPlayGoChunkId* chunkIds,
u32 numberOfEntries, OrbisPlayGoLocus minimumLocus);
s32 PS4_SYSV_ABI scePlayGoSetInstallSpeed(OrbisPlayGoHandle handle, OrbisPlayGoInstallSpeed speed);
s32 PS4_SYSV_ABI scePlayGoSetLanguageMask(OrbisPlayGoHandle handle,
OrbisPlayGoLanguageMask languageMask);
s32 PS4_SYSV_ABI scePlayGoSetToDoList(OrbisPlayGoHandle handle, const OrbisPlayGoToDo* todoList,

View file

@ -7,6 +7,7 @@
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "rtc.h"
#include "rtc_error.h"
namespace Libraries::Rtc {
@ -123,8 +124,7 @@ int PS4_SYSV_ABI sceRtcGetTick() {
}
int PS4_SYSV_ABI sceRtcGetTickResolution() {
LOG_ERROR(Lib_Rtc, "(STUBBED) called");
return ORBIS_OK;
return 1000000;
}
int PS4_SYSV_ABI sceRtcGetTime_t() {

View file

@ -0,0 +1,17 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
constexpr int ORBIS_RTC_ERROR_INVALID_PARAMETER = 0x80010602;
constexpr int ORBIS_RTC_ERROR_INVALID_TICK_PARAMETER = 0x80010603;
constexpr int ORBIS_RTC_ERROR_INVALID_DATE_PARAMETER = 0x80010604;
constexpr int ORBIS_RTC_ERROR_NOT_IMPLEMENTED = 0x80010605;
constexpr int ORBIS_RTC_ERROR_INVALID_TIMEZONE_FORMAT = 0x80010607;
constexpr int ORBIS_RTC_ERROR_INVALID_YEARS_PARAMETER = 0x80010621;
constexpr int ORBIS_RTC_ERROR_INVALID_MONTHS_PARAMETER = 0x80010622;
constexpr int ORBIS_RTC_ERROR_INVALID_DAYS_PARAMETER = 0x80010623;
constexpr int ORBIS_RTC_ERROR_INVALID_HOURS_PARAMETER = 0x80010624;
constexpr int ORBIS_RTC_ERROR_INVALID_MINUTES_PARAMETER = 0x80010625;
constexpr int ORBIS_RTC_ERROR_INVALID_SECONDS_PARAMETER = 0x80010626;
constexpr int ORBIS_RTC_ERROR_INVALID_MILLISECONDS_PARAMETER = 0x80010627;

View file

@ -6,6 +6,8 @@
#include "core/libraries/libs.h"
#include "core/libraries/system/msgdialog.h"
#include <magic_enum.hpp>
namespace Libraries::MsgDialog {
int PS4_SYSV_ABI sceMsgDialogClose() {
@ -30,9 +32,22 @@ int PS4_SYSV_ABI sceMsgDialogInitialize() {
s32 PS4_SYSV_ABI sceMsgDialogOpen(const OrbisMsgDialogParam* param) {
LOG_ERROR(Lib_MsgDlg, "(STUBBED) called");
OrbisMsgDialogUserMessageParam* userMsgParam = param->userMsgParam;
const char* msg = userMsgParam->msg;
printf("sceMsgDialogOpen msg : %s", msg);
switch (param->mode) {
case ORBIS_MSG_DIALOG_MODE_USER_MSG:
LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen userMsg type = %s msg = %s",
magic_enum::enum_name(param->userMsgParam->buttonType), param->userMsgParam->msg);
break;
case ORBIS_MSG_DIALOG_MODE_PROGRESS_BAR:
LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen progressBar type = %s msg = %s",
magic_enum::enum_name(param->progBarParam->barType), param->progBarParam->msg);
break;
case ORBIS_MSG_DIALOG_MODE_SYSTEM_MSG:
LOG_INFO(Lib_MsgDlg, "sceMsgDialogOpen systemMsg type: %s",
magic_enum::enum_name(param->sysMsgParam->sysMsgType));
break;
default:
break;
}
return ORBIS_OK;
}

View file

@ -34,9 +34,6 @@ Frontend::WindowSDL* g_window = nullptr;
namespace Core {
static constexpr s32 WindowWidth = 1280;
static constexpr s32 WindowHeight = 720;
Emulator::Emulator() {
// Read configuration file.
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
@ -55,6 +52,19 @@ Emulator::Emulator() {
LOG_INFO(Loader, "Branch {}", Common::g_scm_branch);
LOG_INFO(Loader, "Description {}", Common::g_scm_desc);
LOG_INFO(Config, "General isNeo: {}", Config::isNeoMode());
LOG_INFO(Config, "GPU isNullGpu: {}", Config::nullGpu());
LOG_INFO(Config, "GPU shouldDumpShaders: {}", Config::dumpShaders());
LOG_INFO(Config, "GPU shouldDumpPM4: {}", Config::dumpPM4());
LOG_INFO(Config, "GPU vblankDivider: {}", Config::vblankDiv());
LOG_INFO(Config, "Vulkan gpuId: {}", Config::getGpuId());
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 rdocEnable: {}", Config::isRdocEnabled());
LOG_INFO(Config, "Vulkan rdocMarkersEnable: {}", Config::isMarkersEnabled());
LOG_INFO(Config, "LLE isLibc: {}", Config::isLleLibc());
// Defer until after logging is initialized.
memory = Core::Memory::Instance();
controller = Common::Singleton<Input::GameController>::Instance();
@ -91,8 +101,11 @@ void Emulator::Run(const std::filesystem::path& file) {
app_version = param_sfo->GetString("APP_VER");
LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version);
} else if (entry.path().filename() == "playgo-chunk.dat") {
auto* playgo = Common::Singleton<PlaygoChunk>::Instance();
playgo->Open(sce_sys_folder.string() + "/playgo-chunk.dat");
auto* playgo = Common::Singleton<PlaygoFile>::Instance();
auto filepath = sce_sys_folder / "playgo-chunk.dat";
if (!playgo->Open(filepath)) {
LOG_ERROR(Loader, "PlayGo: unable to open file");
}
} else if (entry.path().filename() == "pic0.png" ||
entry.path().filename() == "pic1.png") {
auto* splash = Common::Singleton<Splash>::Instance();
@ -114,8 +127,8 @@ void Emulator::Run(const std::filesystem::path& file) {
window_title =
fmt::format("shadPS4 v{} {} | {}", Common::VERSION, Common::g_scm_desc, game_title);
}
window =
std::make_unique<Frontend::WindowSDL>(WindowWidth, WindowHeight, controller, window_title);
window = std::make_unique<Frontend::WindowSDL>(
Config::getScreenWidth(), Config::getScreenHeight(), controller, window_title);
g_window = window.get();

View file

@ -179,6 +179,7 @@ void MainWindow::CreateConnects() {
connect(ui->mw_searchbar, &QLineEdit::textChanged, this, &MainWindow::SearchGameTable);
connect(ui->exitAct, &QAction::triggered, this, &QWidget::close);
connect(ui->refreshGameListAct, &QAction::triggered, this, &MainWindow::RefreshGameTable);
connect(ui->showGameListAct, &QAction::triggered, this, &MainWindow::ShowGameList);
connect(this, &MainWindow::ExtractionFinished, this, &MainWindow::RefreshGameTable);
connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) {
@ -445,6 +446,15 @@ void MainWindow::SearchGameTable(const QString& text) {
}
}
void MainWindow::ShowGameList() {
if (ui->showGameListAct->isChecked()) {
RefreshGameTable();
} else {
m_game_grid_frame->clearContents();
m_game_list_frame->clearContents();
}
};
void MainWindow::RefreshGameTable() {
// m_game_info->m_games.clear();
m_game_info->GetGameInfo(this);

View file

@ -45,6 +45,7 @@ private Q_SLOTS:
void ConfigureGuiFromSettings();
void SaveWindowState() const;
void SearchGameTable(const QString& text);
void ShowGameList();
void RefreshGameTable();
void HandleResize(QResizeEvent* event);

View file

@ -37,6 +37,7 @@ static IR::Condition MakeCondition(Opcode opcode) {
return IR::Condition::Execnz;
case Opcode::S_AND_SAVEEXEC_B64:
case Opcode::S_ANDN2_B64:
case Opcode::V_CMPX_NE_U32:
return IR::Condition::Execnz;
default:
return IR::Condition::True;

View file

@ -848,6 +848,7 @@ struct Liverpool {
u32 raw;
BitField<0, 1, u32> depth_clear_enable;
BitField<1, 1, u32> stencil_clear_enable;
BitField<5, 1, u32> stencil_compress_disable;
BitField<6, 1, u32> depth_compress_disable;
};

View file

@ -5,6 +5,8 @@
#include "video_core/amdgpu/pixel_format.h"
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
#include <magic_enum.hpp>
namespace Vulkan::LiverpoolToVK {
using DepthBuffer = Liverpool::DepthBuffer;
@ -588,6 +590,8 @@ vk::Format AdjustColorBufferFormat(vk::Format base_format,
return is_vo_surface ? vk::Format::eB8G8R8A8Unorm : vk::Format::eB8G8R8A8Srgb;
case vk::Format::eB8G8R8A8Srgb:
return is_vo_surface ? vk::Format::eR8G8B8A8Unorm : vk::Format::eR8G8B8A8Srgb;
default:
break;
}
} else {
if (is_vo_surface && base_format == vk::Format::eR8G8B8A8Srgb) {
@ -601,27 +605,29 @@ vk::Format AdjustColorBufferFormat(vk::Format base_format,
}
vk::Format DepthFormat(DepthBuffer::ZFormat z_format, DepthBuffer::StencilFormat stencil_format) {
if (z_format == DepthBuffer::ZFormat::Z32Float &&
stencil_format == DepthBuffer::StencilFormat::Stencil8) {
using ZFormat = DepthBuffer::ZFormat;
using StencilFormat = DepthBuffer::StencilFormat;
if (z_format == ZFormat::Z32Float && stencil_format == StencilFormat::Stencil8) {
return vk::Format::eD32SfloatS8Uint;
}
if (z_format == DepthBuffer::ZFormat::Z32Float &&
stencil_format == DepthBuffer::StencilFormat::Invalid) {
if (z_format == ZFormat::Z32Float && stencil_format == StencilFormat::Invalid) {
return vk::Format::eD32Sfloat;
}
if (z_format == DepthBuffer::ZFormat::Z16 &&
stencil_format == DepthBuffer::StencilFormat::Invalid) {
if (z_format == ZFormat::Z16 && stencil_format == StencilFormat::Invalid) {
return vk::Format::eD16Unorm;
}
if (z_format == DepthBuffer::ZFormat::Z16 &&
stencil_format == DepthBuffer::StencilFormat::Stencil8) {
if (z_format == ZFormat::Z16 && stencil_format == StencilFormat::Stencil8) {
return vk::Format::eD16UnormS8Uint;
}
if (z_format == DepthBuffer::ZFormat::Invalid &&
stencil_format == DepthBuffer::StencilFormat::Invalid) {
if (z_format == ZFormat::Invalid && stencil_format == StencilFormat::Stencil8) {
return vk::Format::eD32SfloatS8Uint;
}
if (z_format == ZFormat::Invalid && stencil_format == StencilFormat::Invalid) {
return vk::Format::eUndefined;
}
UNREACHABLE();
UNREACHABLE_MSG("Unsupported depth/stencil format. depth = {} stencil = {}",
magic_enum::enum_name(z_format), magic_enum::enum_name(stencil_format));
}
void EmitQuadToTriangleListIndices(u8* out_ptr, u32 num_vertices) {

View file

@ -214,8 +214,7 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul
.colorAttachmentCount = num_color_formats,
.pColorAttachmentFormats = key.color_formats.data(),
.depthAttachmentFormat = key.depth_format,
.stencilAttachmentFormat =
key.depth.stencil_enable ? key.depth_format : vk::Format::eUndefined,
.stencilAttachmentFormat = key.stencil_format,
};
std::array<vk::PipelineColorBlendAttachmentState, Liverpool::NumColorBuffers> attachments;

View file

@ -26,6 +26,7 @@ struct GraphicsPipelineKey {
std::array<size_t, MaxShaderStages> stage_hashes;
std::array<vk::Format, Liverpool::NumColorBuffers> color_formats;
vk::Format depth_format;
vk::Format stencil_format;
Liverpool::DepthControl depth;
float depth_bounds_min;

View file

@ -180,11 +180,26 @@ void PipelineCache::RefreshGraphicsKey() {
key.num_samples = regs.aa_config.NumSamples();
const auto& db = regs.depth_buffer;
key.depth_format = LiverpoolToVK::DepthFormat(db.z_info.format, db.stencil_info.format);
const auto ds_format = LiverpoolToVK::DepthFormat(db.z_info.format, db.stencil_info.format);
if (db.z_info.format != AmdGpu::Liverpool::DepthBuffer::ZFormat::Invalid) {
key.depth_format = ds_format;
} else {
key.depth_format = vk::Format::eUndefined;
}
if (key.depth.depth_enable) {
key.depth.depth_enable.Assign(key.depth_format != vk::Format::eUndefined);
}
if (db.stencil_info.format != AmdGpu::Liverpool::DepthBuffer::StencilFormat::Invalid) {
key.stencil_format = key.depth_format;
} else {
key.stencil_format = vk::Format::eUndefined;
}
if (key.depth.stencil_enable) {
key.depth.stencil_enable.Assign(key.stencil_format != vk::Format::eUndefined);
}
const auto skip_cb_binding =
regs.color_control.mode == AmdGpu::Liverpool::ColorControl::OperationMode::Disable;

View file

@ -29,15 +29,22 @@ void Scheduler::BeginRendering(const RenderState& new_state) {
is_rendering = true;
render_state = new_state;
const auto witdh =
render_state.width != std::numeric_limits<u32>::max() ? render_state.width : 1;
const auto height =
render_state.height != std::numeric_limits<u32>::max() ? render_state.height : 1;
const vk::RenderingInfo rendering_info = {
.renderArea =
{
.offset = {0, 0},
.extent = {render_state.width, render_state.height},
.extent = {witdh, height},
},
.layerCount = 1,
.colorAttachmentCount = render_state.num_color_attachments,
.pColorAttachments = render_state.color_attachments.data(),
.pColorAttachments = render_state.num_color_attachments > 0
? render_state.color_attachments.data()
: nullptr,
.pDepthAttachment = render_state.has_depth ? &render_state.depth_attachment : nullptr,
.pStencilAttachment = render_state.has_stencil ? &render_state.depth_attachment : nullptr,
};
@ -72,7 +79,7 @@ void Scheduler::EndRendering() {
},
});
}
if (render_state.has_depth) {
if (render_state.has_depth || render_state.has_stencil) {
barriers.push_back(vk::ImageMemoryBarrier{
.srcAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite,
.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite,

View file

@ -131,11 +131,19 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_,
usage = ImageUsageFlags(info);
if (info.pixel_format == vk::Format::eD32Sfloat) {
switch (info.pixel_format) {
case vk::Format::eD16Unorm:
case vk::Format::eD32Sfloat:
case vk::Format::eX8D24UnormPack32:
aspect_mask = vk::ImageAspectFlagBits::eDepth;
}
if (info.pixel_format == vk::Format::eD32SfloatS8Uint) {
break;
case vk::Format::eD16UnormS8Uint:
case vk::Format::eD24UnormS8Uint:
case vk::Format::eD32SfloatS8Uint:
aspect_mask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
break;
default:
break;
}
const vk::ImageCreateInfo image_ci = {