mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-12-16 12:09:07 +00:00
Merge branch 'main' into m4aac
This commit is contained in:
commit
9278381bb3
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -108,8 +108,12 @@
|
||||
branch = dist
|
||||
[submodule "externals/MoltenVK"]
|
||||
path = externals/MoltenVK
|
||||
url = https://github.com/KhronosGroup/MoltenVK.git
|
||||
url = https://github.com/shadPS4-emu/ext-MoltenVK.git
|
||||
shallow = true
|
||||
[submodule "externals/json"]
|
||||
path = externals/json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
[submodule "externals/sdl3_mixer"]
|
||||
path = externals/sdl3_mixer
|
||||
url = https://github.com/libsdl-org/SDL_mixer
|
||||
shallow = true
|
||||
|
||||
@ -203,7 +203,7 @@ execute_process(
|
||||
# Set Version
|
||||
set(EMULATOR_VERSION_MAJOR "0")
|
||||
set(EMULATOR_VERSION_MINOR "12")
|
||||
set(EMULATOR_VERSION_PATCH "1")
|
||||
set(EMULATOR_VERSION_PATCH "6")
|
||||
|
||||
set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}")
|
||||
|
||||
@ -229,6 +229,7 @@ find_package(magic_enum 0.9.7 CONFIG)
|
||||
find_package(PNG 1.6 MODULE)
|
||||
find_package(RenderDoc 1.6.0 MODULE)
|
||||
find_package(SDL3 3.1.2 CONFIG)
|
||||
find_package(SDL3_mixer 2.8.1 CONFIG)
|
||||
find_package(stb MODULE)
|
||||
find_package(toml11 4.2.0 CONFIG)
|
||||
find_package(tsl-robin-map 1.3.0 CONFIG)
|
||||
@ -463,6 +464,12 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
|
||||
src/core/libraries/mouse/mouse.h
|
||||
src/core/libraries/web_browser_dialog/webbrowserdialog.cpp
|
||||
src/core/libraries/web_browser_dialog/webbrowserdialog.h
|
||||
src/core/libraries/font/font.cpp
|
||||
src/core/libraries/font/font.h
|
||||
src/core/libraries/font/fontft.cpp
|
||||
src/core/libraries/font/fontft.h
|
||||
src/core/libraries/font/font_error.h
|
||||
|
||||
)
|
||||
|
||||
set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h
|
||||
@ -535,6 +542,10 @@ set(RANDOM_LIB src/core/libraries/random/random.cpp
|
||||
set(USBD_LIB src/core/libraries/usbd/usbd.cpp
|
||||
src/core/libraries/usbd/usbd.h
|
||||
src/core/libraries/usbd/usb_backend.h
|
||||
src/core/libraries/usbd/emulated/dimensions.cpp
|
||||
src/core/libraries/usbd/emulated/dimensions.h
|
||||
src/core/libraries/usbd/emulated/infinity.cpp
|
||||
src/core/libraries/usbd/emulated/infinity.h
|
||||
src/core/libraries/usbd/emulated/skylander.cpp
|
||||
src/core/libraries/usbd/emulated/skylander.h
|
||||
)
|
||||
@ -951,6 +962,8 @@ set(VIDEO_CORE src/video_core/amdgpu/cb_db_extent.h
|
||||
src/video_core/buffer_cache/buffer.h
|
||||
src/video_core/buffer_cache/buffer_cache.cpp
|
||||
src/video_core/buffer_cache/buffer_cache.h
|
||||
src/video_core/buffer_cache/fault_manager.cpp
|
||||
src/video_core/buffer_cache/fault_manager.h
|
||||
src/video_core/buffer_cache/memory_tracker.h
|
||||
src/video_core/buffer_cache/range_set.h
|
||||
src/video_core/buffer_cache/region_definitions.h
|
||||
@ -1062,7 +1075,7 @@ add_executable(shadps4
|
||||
create_target_directory_groups(shadps4)
|
||||
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 SDL3_mixer::SDL3_mixer pugixml::pugixml stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json)
|
||||
|
||||
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
|
||||
target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")
|
||||
|
||||
3
dist/net.shadps4.shadPS4.metainfo.xml
vendored
3
dist/net.shadps4.shadPS4.metainfo.xml
vendored
@ -37,6 +37,9 @@
|
||||
<category translate="no">Game</category>
|
||||
</categories>
|
||||
<releases>
|
||||
<release version="0.12.5" date="2025-11-07">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.12.5</url>
|
||||
</release>
|
||||
<release version="0.12.0" date="2025-10-31">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.12.0</url>
|
||||
</release>
|
||||
|
||||
@ -36,16 +36,11 @@ Go through the Git for Windows installation as normal
|
||||
|
||||
1. Open up Visual Studio, select `Open a local folder` and select the folder with the shadPS4 source code. The folder should contain `CMakeLists.txt`
|
||||
2. Change Clang x64 Debug to Clang x64 Release if you want a regular, non-debug build.
|
||||
3. If you want to build shadPS4 with the Qt Gui, simply select Clang x64 Release with Qt instead.
|
||||
4. Change the project to build to shadps4.exe
|
||||
5. Build -> Build All
|
||||
3. Change the project to build to shadps4.exe
|
||||
4. Build -> Build All
|
||||
|
||||
Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\`
|
||||
|
||||
To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal:
|
||||
`C:\Qt\<QtVersion>\msvc2022_64\bin\windeployqt6.exe "C:\path\to\shadps4.exe"`
|
||||
(Change Qt path if you've installed it to non-default path)
|
||||
|
||||
## Option 2: MSYS2/MinGW
|
||||
|
||||
> [!IMPORTANT]
|
||||
@ -73,7 +68,6 @@ 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-rapidjson mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg`
|
||||
1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools mingw-w64-clang-aarch64-qt6-multimedia`
|
||||
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
|
||||
5. Run `cd shadPS4`
|
||||
6. Run `cmake -S . -B build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"`
|
||||
|
||||
12
externals/CMakeLists.txt
vendored
12
externals/CMakeLists.txt
vendored
@ -63,6 +63,18 @@ if (NOT TARGET SDL3::SDL3)
|
||||
add_subdirectory(sdl3)
|
||||
endif()
|
||||
|
||||
# SDL3_mixer
|
||||
if (NOT TARGET SDL3_mixer::SDL3_mixer)
|
||||
set(SDLMIXER_FLAC OFF)
|
||||
set(SDLMIXER_OGG OFF)
|
||||
set(SDLMIXER_MOD OFF)
|
||||
set(SDLMIXER_MIDI OFF)
|
||||
set(SDLMIXER_OPUS OFF)
|
||||
set(SDLMIXER_WAVPACK OFF)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
add_subdirectory(sdl3_mixer)
|
||||
endif()
|
||||
|
||||
# vulkan-headers
|
||||
if (NOT TARGET Vulkan::Headers)
|
||||
set(VULKAN_HEADERS_ENABLE_MODULE OFF)
|
||||
|
||||
2
externals/MoltenVK
vendored
2
externals/MoltenVK
vendored
@ -1 +1 @@
|
||||
Subproject commit b23d42534622cd9926fe526fec1b7f8795a2853c
|
||||
Subproject commit f168dec05998ab0ca09a400bab6831a95c0bdb2e
|
||||
1
externals/sdl3_mixer
vendored
Submodule
1
externals/sdl3_mixer
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 4182794ea45fe28568728670c6f1583855d0e85c
|
||||
65
shell.nix
65
shell.nix
@ -6,42 +6,43 @@ with import (fetchTarball "https://github.com/nixos/nixpkgs/archive/cfd19cdc5468
|
||||
pkgs.mkShell {
|
||||
name = "shadps4-build-env";
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkgs.llvmPackages_18.clang
|
||||
pkgs.cmake
|
||||
pkgs.pkg-config
|
||||
pkgs.git
|
||||
nativeBuildInputs = with pkgs; [
|
||||
llvmPackages_18.clang
|
||||
cmake
|
||||
pkg-config
|
||||
git
|
||||
util-linux
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
pkgs.alsa-lib
|
||||
pkgs.libpulseaudio
|
||||
pkgs.openal
|
||||
pkgs.zlib
|
||||
pkgs.libedit
|
||||
pkgs.udev
|
||||
pkgs.libevdev
|
||||
pkgs.SDL2
|
||||
pkgs.jack2
|
||||
pkgs.sndio
|
||||
buildInputs = with pkgs; [
|
||||
alsa-lib
|
||||
libpulseaudio
|
||||
openal
|
||||
zlib
|
||||
libedit
|
||||
udev
|
||||
libevdev
|
||||
SDL2
|
||||
jack2
|
||||
sndio
|
||||
|
||||
pkgs.vulkan-headers
|
||||
pkgs.vulkan-utility-libraries
|
||||
pkgs.vulkan-tools
|
||||
vulkan-headers
|
||||
vulkan-utility-libraries
|
||||
vulkan-tools
|
||||
|
||||
pkgs.ffmpeg
|
||||
pkgs.fmt
|
||||
pkgs.glslang
|
||||
pkgs.libxkbcommon
|
||||
pkgs.wayland
|
||||
pkgs.xorg.libxcb
|
||||
pkgs.xorg.xcbutil
|
||||
pkgs.xorg.xcbutilkeysyms
|
||||
pkgs.xorg.xcbutilwm
|
||||
pkgs.sdl3
|
||||
pkgs.stb
|
||||
pkgs.wayland-protocols
|
||||
pkgs.libpng
|
||||
ffmpeg
|
||||
fmt
|
||||
glslang
|
||||
libxkbcommon
|
||||
wayland
|
||||
xorg.libxcb
|
||||
xorg.xcbutil
|
||||
xorg.xcbutilkeysyms
|
||||
xorg.xcbutilwm
|
||||
sdl3
|
||||
stb
|
||||
wayland-protocols
|
||||
libpng
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
|
||||
@ -68,6 +68,7 @@ class ElfInfo {
|
||||
std::string app_ver{};
|
||||
u32 firmware_ver = 0;
|
||||
u32 raw_firmware_ver = 0;
|
||||
u32 sdk_ver = 0;
|
||||
PSFAttributes psf_attributes{};
|
||||
|
||||
std::filesystem::path splash_path{};
|
||||
@ -117,6 +118,11 @@ public:
|
||||
return raw_firmware_ver;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 CompiledSdkVer() const {
|
||||
ASSERT(initialized);
|
||||
return sdk_ver;
|
||||
}
|
||||
|
||||
[[nodiscard]] const PSFAttributes& GetPSFAttributes() const {
|
||||
ASSERT(initialized);
|
||||
return psf_attributes;
|
||||
|
||||
@ -40,28 +40,30 @@ namespace {
|
||||
switch (mode) {
|
||||
case FileAccessMode::Read:
|
||||
return L"rb";
|
||||
case FileAccessMode::Write:
|
||||
return L"wb";
|
||||
case FileAccessMode::Append:
|
||||
return L"ab";
|
||||
case FileAccessMode::Write:
|
||||
case FileAccessMode::ReadWrite:
|
||||
return L"r+b";
|
||||
case FileAccessMode::ReadAppend:
|
||||
return L"a+b";
|
||||
case FileAccessMode::Create:
|
||||
return L"wb";
|
||||
}
|
||||
break;
|
||||
case FileType::TextFile:
|
||||
switch (mode) {
|
||||
case FileAccessMode::Read:
|
||||
return L"r";
|
||||
case FileAccessMode::Write:
|
||||
return L"w";
|
||||
case FileAccessMode::Append:
|
||||
return L"a";
|
||||
case FileAccessMode::Write:
|
||||
case FileAccessMode::ReadWrite:
|
||||
return L"r+";
|
||||
case FileAccessMode::ReadAppend:
|
||||
return L"a+";
|
||||
case FileAccessMode::Create:
|
||||
return L"w";
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -91,28 +93,30 @@ namespace {
|
||||
switch (mode) {
|
||||
case FileAccessMode::Read:
|
||||
return "rb";
|
||||
case FileAccessMode::Write:
|
||||
return "wb";
|
||||
case FileAccessMode::Append:
|
||||
return "ab";
|
||||
case FileAccessMode::Write:
|
||||
case FileAccessMode::ReadWrite:
|
||||
return "r+b";
|
||||
case FileAccessMode::ReadAppend:
|
||||
return "a+b";
|
||||
case FileAccessMode::Create:
|
||||
return "wb";
|
||||
}
|
||||
break;
|
||||
case FileType::TextFile:
|
||||
switch (mode) {
|
||||
case FileAccessMode::Read:
|
||||
return "r";
|
||||
case FileAccessMode::Write:
|
||||
return "w";
|
||||
case FileAccessMode::Append:
|
||||
return "a";
|
||||
case FileAccessMode::Write:
|
||||
case FileAccessMode::ReadWrite:
|
||||
return "r+";
|
||||
case FileAccessMode::ReadAppend:
|
||||
return "a+";
|
||||
case FileAccessMode::Create:
|
||||
return "w";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -21,9 +21,8 @@ enum class FileAccessMode {
|
||||
*/
|
||||
Read = 1 << 0,
|
||||
/**
|
||||
* If the file at path exists, the existing contents of the file are erased.
|
||||
* The empty file is then opened for writing.
|
||||
* If the file at path does not exist, it creates and opens a new empty file for writing.
|
||||
* If the file at path exists, it opens the file for writing.
|
||||
* If the file at path does not exist, it fails to open the file.
|
||||
*/
|
||||
Write = 1 << 1,
|
||||
/**
|
||||
@ -42,6 +41,12 @@ enum class FileAccessMode {
|
||||
* reading and appending.
|
||||
*/
|
||||
ReadAppend = Read | Append,
|
||||
/**
|
||||
* If the file at path exists, the existing contents of the file are erased.
|
||||
* The empty file is then opened for writing.
|
||||
* If the file at path does not exist, it creates and opens a new empty file for writing.
|
||||
*/
|
||||
Create = 1 << 3,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(FileAccessMode);
|
||||
|
||||
@ -102,6 +107,11 @@ public:
|
||||
return file != nullptr;
|
||||
}
|
||||
|
||||
bool IsWriteOnly() const {
|
||||
return file_access_mode == FileAccessMode::Append ||
|
||||
file_access_mode == FileAccessMode::Write;
|
||||
}
|
||||
|
||||
uintptr_t GetFileMapping();
|
||||
|
||||
int Open(const std::filesystem::path& path, FileAccessMode mode,
|
||||
@ -210,7 +220,7 @@ public:
|
||||
}
|
||||
|
||||
static size_t WriteBytes(const std::filesystem::path path, const auto& data) {
|
||||
IOFile out(path, FileAccessMode::Write);
|
||||
IOFile out(path, FileAccessMode::Create);
|
||||
return out.Write(data);
|
||||
}
|
||||
std::FILE* file = nullptr;
|
||||
|
||||
@ -62,7 +62,7 @@ private:
|
||||
class FileBackend {
|
||||
public:
|
||||
explicit FileBackend(const std::filesystem::path& filename, bool should_append = false)
|
||||
: file{filename, should_append ? FS::FileAccessMode::Append : FS::FileAccessMode::Write,
|
||||
: file{filename, should_append ? FS::FileAccessMode::Append : FS::FileAccessMode::Create,
|
||||
FS::FileType::TextFile} {}
|
||||
|
||||
~FileBackend() = default;
|
||||
@ -182,7 +182,13 @@ public:
|
||||
}
|
||||
|
||||
void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
||||
const char* function, std::string message) {
|
||||
const char* function, const char* format, const fmt::format_args& args) {
|
||||
if (!filter.CheckMessage(log_class, log_level) || !Config::getLoggingEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto message = fmt::vformat(format, args);
|
||||
|
||||
// Propagate important log messages to the profiler
|
||||
if (IsProfilerConnected()) {
|
||||
const auto& msg_str = fmt::format("[{}] {}", GetLogClassName(log_class), message);
|
||||
@ -201,10 +207,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (!filter.CheckMessage(log_class, log_level) || !Config::getLoggingEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::microseconds;
|
||||
using std::chrono::steady_clock;
|
||||
@ -324,8 +326,8 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
||||
unsigned int line_num, const char* function, const char* format,
|
||||
const fmt::format_args& args) {
|
||||
if (!initialization_in_progress_suppress_logging) [[likely]] {
|
||||
Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function,
|
||||
fmt::vformat(format, args));
|
||||
Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, format,
|
||||
args);
|
||||
}
|
||||
}
|
||||
} // namespace Common::Log
|
||||
|
||||
@ -140,6 +140,8 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Lib, NpParty) \
|
||||
SUB(Lib, Zlib) \
|
||||
SUB(Lib, Hmd) \
|
||||
SUB(Lib, Font) \
|
||||
SUB(Lib, FontFt) \
|
||||
SUB(Lib, HmdSetupDialog) \
|
||||
SUB(Lib, SigninDialog) \
|
||||
SUB(Lib, Camera) \
|
||||
|
||||
@ -114,6 +114,8 @@ enum class Class : u8 {
|
||||
Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation.
|
||||
Lib_CompanionUtil, ///< The LibCompanionUtil implementation.
|
||||
Lib_VrTracker, ///< The LibSceVrTracker implementation.
|
||||
Lib_Font, ///< The libSceFont implementation.
|
||||
Lib_FontFt, ///< The libSceFontFt implementation.
|
||||
Frontend, ///< Emulator UI
|
||||
Render, ///< Video Core
|
||||
Render_Vulkan, ///< Vulkan backend
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/elf_info.h"
|
||||
#include "common/error.h"
|
||||
#include "core/address_space.h"
|
||||
#include "core/libraries/kernel/memory.h"
|
||||
@ -103,8 +104,8 @@ struct AddressSpace::Impl {
|
||||
GetSystemInfo(&sys_info);
|
||||
u64 alignment = sys_info.dwAllocationGranularity;
|
||||
|
||||
// Determine the host OS build number
|
||||
// Retrieve module handle for ntdll
|
||||
// Older Windows builds have a severe performance issue with VirtualAlloc2.
|
||||
// We need to get the host's Windows version, then determine if it needs a workaround.
|
||||
auto ntdll_handle = GetModuleHandleW(L"ntdll.dll");
|
||||
ASSERT_MSG(ntdll_handle, "Failed to retrieve ntdll handle");
|
||||
|
||||
@ -120,12 +121,20 @@ struct AddressSpace::Impl {
|
||||
u64 supported_user_max = USER_MAX;
|
||||
// This is the build number for Windows 11 22H2
|
||||
static constexpr s32 AffectedBuildNumber = 22621;
|
||||
if (os_version_info.dwBuildNumber <= AffectedBuildNumber) {
|
||||
// Older Windows builds have an issue with VirtualAlloc2 on higher addresses.
|
||||
// To prevent regressions, limit the maximum address we reserve for this platform.
|
||||
supported_user_max = 0x11000000000ULL;
|
||||
LOG_WARNING(Core, "Windows 10 detected, reducing user max to {:#x} to avoid problems",
|
||||
supported_user_max);
|
||||
|
||||
// Higher PS4 firmware versions prevent higher address mappings too.
|
||||
s32 sdk_ver = Common::ElfInfo::Instance().CompiledSdkVer();
|
||||
if (os_version_info.dwBuildNumber <= AffectedBuildNumber ||
|
||||
sdk_ver >= Common::ElfInfo::FW_30) {
|
||||
supported_user_max = 0x10000000000ULL;
|
||||
// Only log the message if we're restricting the user max due to operating system.
|
||||
// Since higher compiled SDK versions also get reduced max, we don't need to log there.
|
||||
if (sdk_ver < Common::ElfInfo::FW_30) {
|
||||
LOG_WARNING(
|
||||
Core,
|
||||
"Older Windows version detected, reducing user max to {:#x} to avoid problems",
|
||||
supported_user_max);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the free address ranges we can access.
|
||||
|
||||
@ -152,7 +152,7 @@ inline std::string RunDisassembler(const std::string& disassembler_cli, const T&
|
||||
}
|
||||
} else {
|
||||
cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\"");
|
||||
Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write);
|
||||
Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Create);
|
||||
file.Write(shader_code);
|
||||
file.Close();
|
||||
|
||||
|
||||
@ -123,7 +123,7 @@ void FrameDumpViewer::Draw() {
|
||||
const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time,
|
||||
magic_enum::enum_name(selected_queue_type),
|
||||
selected_submit_num, selected_queue_num2);
|
||||
Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write);
|
||||
Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Create);
|
||||
const auto& data = frame_dump->queues[selected_cmd].data;
|
||||
if (file.IsOpen()) {
|
||||
DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname));
|
||||
|
||||
@ -99,7 +99,7 @@ bool PSF::Open(const std::vector<u8>& psf_buffer) {
|
||||
}
|
||||
|
||||
bool PSF::Encode(const std::filesystem::path& filepath) const {
|
||||
Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Write);
|
||||
Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Create);
|
||||
if (!file.IsOpen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
1611
src/core/libraries/font/font.cpp
Normal file
1611
src/core/libraries/font/font.cpp
Normal file
File diff suppressed because it is too large
Load Diff
299
src/core/libraries/font/font.h
Normal file
299
src/core/libraries/font/font.h
Normal file
@ -0,0 +1,299 @@
|
||||
// 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::Font {
|
||||
|
||||
struct OrbisFontTextCharacter {
|
||||
// Other fields...
|
||||
struct OrbisFontTextCharacter* next; // Pointer to the next node 0x00
|
||||
struct OrbisFontTextCharacter* prev; // Pointer to the next node 0x08
|
||||
void* textOrder; // Field at offset 0x10 (pointer to text order info)
|
||||
u32 characterCode; // Field assumed at offset 0x28
|
||||
u8 unkn_0x31; // Offset 0x31
|
||||
u8 unkn_0x33; // Offset 0x33
|
||||
u8 charType; // Field assumed at offset 0x39
|
||||
u8 bidiLevel; // Field assumed at offset 0x3B stores the Bidi level
|
||||
u8 formatFlags; // Field at offset 0x3D (stores format-related flags)
|
||||
};
|
||||
|
||||
struct OrbisFontRenderSurface {
|
||||
void* buffer;
|
||||
s32 widthByte;
|
||||
s8 pixelSizeByte;
|
||||
u8 unkn_0xd;
|
||||
u8 styleFlag;
|
||||
u8 unkn_0xf;
|
||||
s32 width, height;
|
||||
u32 sc_x0;
|
||||
u32 sc_y0;
|
||||
u32 sc_x1;
|
||||
u32 sc_y1;
|
||||
void* unkn_28[3];
|
||||
};
|
||||
|
||||
struct OrbisFontStyleFrame {
|
||||
/*0x00*/ u16 magic; // Expected to be 0xF09
|
||||
/*0x02*/ u16 flags;
|
||||
/*0x04*/ s32 dpiX; // DPI scaling factor for width
|
||||
/*0x08*/ s32 dpiY; // DPI scaling factor for height
|
||||
/*0x0c*/ s32 scalingFlag; // Indicates whether scaling is enabled
|
||||
/*0x10*/
|
||||
/*0x14*/ float scaleWidth; // Width scaling factor
|
||||
/*0x18*/ float scaleHeight; // Height scaling factor
|
||||
/*0x1c*/ float weightXScale;
|
||||
/*0x20*/ float weightYScale;
|
||||
/*0x24*/ float slantRatio;
|
||||
};
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer();
|
||||
s32 PS4_SYSV_ABI sceFontBindRenderer();
|
||||
s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel(OrbisFontTextCharacter* textCharacter,
|
||||
int* bidiLevel);
|
||||
s32 PS4_SYSV_ABI sceFontCharacterGetSyllableStringState();
|
||||
s32 PS4_SYSV_ABI sceFontCharacterGetTextFontCode();
|
||||
s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder(OrbisFontTextCharacter* textCharacter,
|
||||
void** pTextOrder);
|
||||
u32 PS4_SYSV_ABI sceFontCharacterLooksFormatCharacters(OrbisFontTextCharacter* textCharacter);
|
||||
u32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace(OrbisFontTextCharacter* textCharacter);
|
||||
OrbisFontTextCharacter* PS4_SYSV_ABI
|
||||
sceFontCharacterRefersTextBack(OrbisFontTextCharacter* textCharacter);
|
||||
OrbisFontTextCharacter* PS4_SYSV_ABI
|
||||
sceFontCharacterRefersTextNext(OrbisFontTextCharacter* textCharacter);
|
||||
s32 PS4_SYSV_ABI sceFontCharactersRefersTextCodes();
|
||||
s32 PS4_SYSV_ABI sceFontClearDeviceCache();
|
||||
s32 PS4_SYSV_ABI sceFontCloseFont();
|
||||
s32 PS4_SYSV_ABI sceFontControl();
|
||||
s32 PS4_SYSV_ABI sceFontCreateGraphicsDevice();
|
||||
s32 PS4_SYSV_ABI sceFontCreateGraphicsService();
|
||||
s32 PS4_SYSV_ABI sceFontCreateGraphicsServiceWithEdition();
|
||||
s32 PS4_SYSV_ABI sceFontCreateLibrary();
|
||||
s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition();
|
||||
s32 PS4_SYSV_ABI sceFontCreateRenderer();
|
||||
s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition();
|
||||
s32 PS4_SYSV_ABI sceFontCreateString();
|
||||
s32 PS4_SYSV_ABI sceFontCreateWords();
|
||||
s32 PS4_SYSV_ABI sceFontCreateWritingLine();
|
||||
s32 PS4_SYSV_ABI sceFontDefineAttribute();
|
||||
s32 PS4_SYSV_ABI sceFontDeleteGlyph();
|
||||
s32 PS4_SYSV_ABI sceFontDestroyGraphicsDevice();
|
||||
s32 PS4_SYSV_ABI sceFontDestroyGraphicsService();
|
||||
s32 PS4_SYSV_ABI sceFontDestroyLibrary();
|
||||
s32 PS4_SYSV_ABI sceFontDestroyRenderer();
|
||||
s32 PS4_SYSV_ABI sceFontDestroyString();
|
||||
s32 PS4_SYSV_ABI sceFontDestroyWords();
|
||||
s32 PS4_SYSV_ABI sceFontDestroyWritingLine();
|
||||
s32 PS4_SYSV_ABI sceFontDettachDeviceCacheBuffer();
|
||||
s32 PS4_SYSV_ABI sceFontGenerateCharGlyph();
|
||||
s32 PS4_SYSV_ABI sceFontGetAttribute();
|
||||
s32 PS4_SYSV_ABI sceFontGetCharGlyphCode();
|
||||
s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics();
|
||||
s32 PS4_SYSV_ABI sceFontGetEffectSlant();
|
||||
s32 PS4_SYSV_ABI sceFontGetEffectWeight();
|
||||
s32 PS4_SYSV_ABI sceFontGetFontGlyphsCount();
|
||||
s32 PS4_SYSV_ABI sceFontGetFontGlyphsOutlineProfile();
|
||||
s32 PS4_SYSV_ABI sceFontGetFontMetrics();
|
||||
s32 PS4_SYSV_ABI sceFontGetFontResolution();
|
||||
s32 PS4_SYSV_ABI sceFontGetFontStyleInformation();
|
||||
s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState();
|
||||
s32 PS4_SYSV_ABI sceFontGetHorizontalLayout();
|
||||
s32 PS4_SYSV_ABI sceFontGetKerning();
|
||||
s32 PS4_SYSV_ABI sceFontGetLibrary();
|
||||
s32 PS4_SYSV_ABI sceFontGetPixelResolution();
|
||||
s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics();
|
||||
s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant();
|
||||
s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight();
|
||||
s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning();
|
||||
s32 PS4_SYSV_ABI sceFontGetRenderScalePixel();
|
||||
s32 PS4_SYSV_ABI sceFontGetRenderScalePoint();
|
||||
s32 PS4_SYSV_ABI sceFontGetResolutionDpi();
|
||||
s32 PS4_SYSV_ABI sceFontGetScalePixel();
|
||||
s32 PS4_SYSV_ABI sceFontGetScalePoint();
|
||||
s32 PS4_SYSV_ABI sceFontGetScriptLanguage();
|
||||
s32 PS4_SYSV_ABI sceFontGetTypographicDesign();
|
||||
s32 PS4_SYSV_ABI sceFontGetVerticalLayout();
|
||||
s32 PS4_SYSV_ABI sceFontGlyphDefineAttribute();
|
||||
s32 PS4_SYSV_ABI sceFontGlyphGetAttribute();
|
||||
s32 PS4_SYSV_ABI sceFontGlyphGetGlyphForm();
|
||||
s32 PS4_SYSV_ABI sceFontGlyphGetMetricsForm();
|
||||
s32 PS4_SYSV_ABI sceFontGlyphGetScalePixel();
|
||||
s32 PS4_SYSV_ABI sceFontGlyphRefersMetrics();
|
||||
s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontal();
|
||||
s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalAdvance();
|
||||
s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalX();
|
||||
s32 PS4_SYSV_ABI sceFontGlyphRefersOutline();
|
||||
s32 PS4_SYSV_ABI sceFontGlyphRenderImage();
|
||||
s32 PS4_SYSV_ABI sceFontGlyphRenderImageHorizontal();
|
||||
s32 PS4_SYSV_ABI sceFontGlyphRenderImageVertical();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsBeginFrame();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsDrawingCancel();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsDrawingFinish();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsEndFrame();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsExchangeResource();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsFillMethodInit();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsFillPlotInit();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetLayout();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetMapping();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsFillRatesInit();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetFillEffect();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetLayout();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetMapping();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsGetDeviceUsage();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsRegionInit();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsRegionInitCircular();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsRegionInitRoundish();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsRelease();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsRenderResource();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsSetFramePolicy();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsSetupClipping();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsSetupColorRates();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsSetupFillMethod();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsSetupFillRates();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFill();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFillPlot();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsSetupHandleDefault();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsSetupLocation();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsSetupPositioning();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsSetupRotation();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsSetupScaling();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFill();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFillPlot();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvas();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvasSequence();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsStructureDesign();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsStructureDesignResource();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsStructureSurfaceTexture();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsUpdateClipping();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsUpdateColorRates();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillMethod();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillRates();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFill();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFillPlot();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsUpdateLocation();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsUpdatePositioning();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsUpdateRotation();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsUpdateScaling();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFill();
|
||||
s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFillPlot();
|
||||
s32 PS4_SYSV_ABI sceFontMemoryInit();
|
||||
s32 PS4_SYSV_ABI sceFontMemoryTerm();
|
||||
s32 PS4_SYSV_ABI sceFontOpenFontFile();
|
||||
s32 PS4_SYSV_ABI sceFontOpenFontInstance();
|
||||
s32 PS4_SYSV_ABI sceFontOpenFontMemory();
|
||||
s32 PS4_SYSV_ABI sceFontOpenFontSet();
|
||||
s32 PS4_SYSV_ABI sceFontRebindRenderer();
|
||||
s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage();
|
||||
s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal();
|
||||
s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical();
|
||||
s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize();
|
||||
s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer();
|
||||
s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy();
|
||||
void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface, void* buffer,
|
||||
int bufWidthByte, int pixelSizeByte, int widthPixel,
|
||||
int heightPixel);
|
||||
void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderSurface, int x0,
|
||||
int y0, int w, int h);
|
||||
s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* renderSurface,
|
||||
OrbisFontStyleFrame* styleFrame);
|
||||
s32 PS4_SYSV_ABI sceFontSetEffectSlant();
|
||||
s32 PS4_SYSV_ABI sceFontSetEffectWeight();
|
||||
s32 PS4_SYSV_ABI sceFontSetFontsOpenMode();
|
||||
s32 PS4_SYSV_ABI sceFontSetResolutionDpi();
|
||||
s32 PS4_SYSV_ABI sceFontSetScalePixel();
|
||||
s32 PS4_SYSV_ABI sceFontSetScalePoint();
|
||||
s32 PS4_SYSV_ABI sceFontSetScriptLanguage();
|
||||
s32 PS4_SYSV_ABI sceFontSetTypographicDesign();
|
||||
s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant();
|
||||
s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight();
|
||||
s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel();
|
||||
s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint();
|
||||
s32 PS4_SYSV_ABI sceFontStringGetTerminateCode();
|
||||
s32 PS4_SYSV_ABI sceFontStringGetTerminateOrder();
|
||||
s32 PS4_SYSV_ABI sceFontStringGetWritingForm();
|
||||
s32 PS4_SYSV_ABI sceFontStringRefersRenderCharacters();
|
||||
s32 PS4_SYSV_ABI sceFontStringRefersTextCharacters();
|
||||
s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(OrbisFontStyleFrame* styleFrame,
|
||||
float* slantRatio);
|
||||
s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(OrbisFontStyleFrame* fontStyleFrame,
|
||||
float* weightXScale, float* weightYScale,
|
||||
uint32_t* mode);
|
||||
s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi();
|
||||
s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(OrbisFontStyleFrame* styleFrame, float* w,
|
||||
float* h);
|
||||
s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePoint();
|
||||
s32 PS4_SYSV_ABI sceFontStyleFrameInit();
|
||||
s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectSlant();
|
||||
s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectWeight();
|
||||
s32 PS4_SYSV_ABI sceFontStyleFrameSetResolutionDpi();
|
||||
s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePixel();
|
||||
s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePoint();
|
||||
s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectSlant();
|
||||
s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectWeight();
|
||||
s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale();
|
||||
s32 PS4_SYSV_ABI sceFontSupportExternalFonts();
|
||||
s32 PS4_SYSV_ABI sceFontSupportGlyphs();
|
||||
s32 PS4_SYSV_ABI sceFontSupportSystemFonts();
|
||||
s32 PS4_SYSV_ABI sceFontTextCodesStepBack();
|
||||
s32 PS4_SYSV_ABI sceFontTextCodesStepNext();
|
||||
s32 PS4_SYSV_ABI sceFontTextSourceInit();
|
||||
s32 PS4_SYSV_ABI sceFontTextSourceRewind();
|
||||
s32 PS4_SYSV_ABI sceFontTextSourceSetDefaultFont();
|
||||
s32 PS4_SYSV_ABI sceFontTextSourceSetWritingForm();
|
||||
s32 PS4_SYSV_ABI sceFontUnbindRenderer();
|
||||
s32 PS4_SYSV_ABI sceFontWordsFindWordCharacters();
|
||||
s32 PS4_SYSV_ABI sceFontWritingGetRenderMetrics();
|
||||
s32 PS4_SYSV_ABI sceFontWritingInit();
|
||||
s32 PS4_SYSV_ABI sceFontWritingLineClear();
|
||||
s32 PS4_SYSV_ABI sceFontWritingLineGetOrderingSpace();
|
||||
s32 PS4_SYSV_ABI sceFontWritingLineGetRenderMetrics();
|
||||
s32 PS4_SYSV_ABI sceFontWritingLineRefersRenderStep();
|
||||
s32 PS4_SYSV_ABI sceFontWritingLineWritesOrder();
|
||||
s32 PS4_SYSV_ABI sceFontWritingRefersRenderStep();
|
||||
s32 PS4_SYSV_ABI sceFontWritingRefersRenderStepCharacter();
|
||||
s32 PS4_SYSV_ABI sceFontWritingSetMaskInvisible();
|
||||
s32 PS4_SYSV_ABI Func_00F4D778F1C88CB3();
|
||||
s32 PS4_SYSV_ABI Func_03C650025FBB0DE7();
|
||||
s32 PS4_SYSV_ABI Func_07EAB8A163B27E1A();
|
||||
s32 PS4_SYSV_ABI Func_09408E88E4F97CE3();
|
||||
s32 PS4_SYSV_ABI Func_09F92905ED82A814();
|
||||
s32 PS4_SYSV_ABI Func_0D142CEE1AB21ABE();
|
||||
s32 PS4_SYSV_ABI Func_14BD2E9E119C16F2();
|
||||
s32 PS4_SYSV_ABI Func_1AC53C9EDEAE8D75();
|
||||
s32 PS4_SYSV_ABI Func_1D401185D5E24C3D();
|
||||
s32 PS4_SYSV_ABI Func_1E83CD20C2CC996F();
|
||||
s32 PS4_SYSV_ABI Func_314B1F765B9FE78A();
|
||||
s32 PS4_SYSV_ABI Func_350E6725FEDE29E1();
|
||||
s32 PS4_SYSV_ABI Func_3DB773F0A604BF39();
|
||||
s32 PS4_SYSV_ABI Func_4FF49DD21E311B1C();
|
||||
s32 PS4_SYSV_ABI Func_526287664A493981();
|
||||
s32 PS4_SYSV_ABI Func_55CA718DBC84A6E9();
|
||||
s32 PS4_SYSV_ABI Func_563FC5F0706A8B4D();
|
||||
s32 PS4_SYSV_ABI Func_569E2ECD34290F45();
|
||||
s32 PS4_SYSV_ABI Func_5A04775B6BE47685();
|
||||
s32 PS4_SYSV_ABI Func_5FD93BCAB6F79750();
|
||||
s32 PS4_SYSV_ABI Func_62B5398F864BD3B4();
|
||||
s32 PS4_SYSV_ABI Func_6F9010294D822367();
|
||||
s32 PS4_SYSV_ABI Func_7757E947423A7A67();
|
||||
s32 PS4_SYSV_ABI Func_7E06BA52077F54FA();
|
||||
s32 PS4_SYSV_ABI Func_93B36DEA021311D6();
|
||||
s32 PS4_SYSV_ABI Func_94B0891E7111598A();
|
||||
s32 PS4_SYSV_ABI Func_9785C9128C2FE7CD();
|
||||
s32 PS4_SYSV_ABI Func_97DFBC9B65FBC0E1();
|
||||
s32 PS4_SYSV_ABI Func_ACD9717405D7D3CA();
|
||||
s32 PS4_SYSV_ABI Func_B19A8AEC3FD4F16F();
|
||||
s32 PS4_SYSV_ABI Func_C10F488AD7CF103D();
|
||||
s32 PS4_SYSV_ABI Func_D0C8B5FF4A6826C7();
|
||||
s32 PS4_SYSV_ABI Func_E48D3CD01C342A33();
|
||||
s32 PS4_SYSV_ABI Func_EAC96B2186B71E14();
|
||||
s32 PS4_SYSV_ABI Func_FE4788A96EF46256();
|
||||
s32 PS4_SYSV_ABI Func_FE7E5AE95D3058F5();
|
||||
|
||||
void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Font
|
||||
44
src/core/libraries/font/font_error.h
Normal file
44
src/core/libraries/font/font_error.h
Normal file
@ -0,0 +1,44 @@
|
||||
// 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_FONT_ERROR_FATAL = 0x80460001;
|
||||
constexpr int ORBIS_FONT_ERROR_INVALID_PARAMETER = 0x80460002;
|
||||
constexpr int ORBIS_FONT_ERROR_INVALID_MEMORY = 0x80460003;
|
||||
constexpr int ORBIS_FONT_ERROR_INVALID_LIBRARY = 0x80460004;
|
||||
constexpr int ORBIS_FONT_ERROR_INVALID_FONT_HANDLE = 0x80460005;
|
||||
constexpr int ORBIS_FONT_ERROR_INVALID_GLYPH = 0x80460006;
|
||||
constexpr int ORBIS_FONT_ERROR_INVALID_RENDERER = 0x80460007;
|
||||
constexpr int ORBIS_FONT_ERROR_INVALID_TEXT_SOURCE = 0x80460008;
|
||||
constexpr int ORBIS_FONT_ERROR_INVALID_STRING = 0x80460009;
|
||||
constexpr int ORBIS_FONT_ERROR_INVALID_WRITING = 0x8046000A;
|
||||
constexpr int ORBIS_FONT_ERROR_INVALID_WORDS = 0x8046000B;
|
||||
constexpr int ORBIS_FONT_ERROR_ALLOCATION_FAILED = 0x80460010;
|
||||
constexpr int ORBIS_FONT_ERROR_FS_OPEN_FAILED = 0x80460011;
|
||||
constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_LIBRARY = 0x80460018;
|
||||
constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_FORMAT = 0x80460019;
|
||||
constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_FUNCTION = 0x80460020;
|
||||
constexpr int ORBIS_FONT_ERROR_ALREADY_SPECIFIED = 0x80460021;
|
||||
constexpr int ORBIS_FONT_ERROR_ALREADY_ATTACHED = 0x80460022;
|
||||
constexpr int ORBIS_FONT_ERROR_ALREADY_OPENED = 0x80460023;
|
||||
constexpr int ORBIS_FONT_ERROR_NOT_ATTACHED_CACHE_BUFFER = 0x80460025;
|
||||
constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_FONTSET = 0x80460031;
|
||||
constexpr int ORBIS_FONT_ERROR_FONT_OPEN_MAX = 0x80460033;
|
||||
constexpr int ORBIS_FONT_ERROR_FONT_OPEN_FAILED = 0x80460036;
|
||||
constexpr int ORBIS_FONT_ERROR_FONT_CLOSE_FAILED = 0x80460037;
|
||||
constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_TYPOGRAPHY = 0x80460040;
|
||||
constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_CODE = 0x80460041;
|
||||
constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_GLYPH = 0x80460042;
|
||||
constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_SCRIPT = 0x80460043;
|
||||
constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_LANGUAGE = 0x80460044;
|
||||
constexpr int ORBIS_FONT_ERROR_NO_SUPPORT_SURFACE = 0x80460050;
|
||||
constexpr int ORBIS_FONT_ERROR_UNSET_PARAMETER = 0x80460058;
|
||||
constexpr int ORBIS_FONT_ERROR_FUNCTIONAL_LIMIT = 0x8046005C;
|
||||
constexpr int ORBIS_FONT_ERROR_ALREADY_BOUND_RENDERER = 0x80460060;
|
||||
constexpr int ORBIS_FONT_ERROR_NOT_BOUND_RENDERER = 0x80460061;
|
||||
constexpr int ORBIS_FONT_ERROR_RENDERER_ALLOCATION_FAILED = 0x80460063;
|
||||
constexpr int ORBIS_FONT_ERROR_RENDERER_ALLOCATION_LIMITED = 0x80460064;
|
||||
constexpr int ORBIS_FONT_ERROR_RENDERER_RENDER_FAILED = 0x80460065;
|
||||
140
src/core/libraries/font/fontft.cpp
Normal file
140
src/core/libraries/font/fontft.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
// 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/font/fontft.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::FontFt {
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtInitAliases() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSetAliasFont() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSetAliasPath() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportBdf() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportCid() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportFontFormats() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportOpenType() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportOpenTypeOtf() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportOpenTypeTtf() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportPcf() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportPfr() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportSystemFonts() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportTrueType() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportTrueTypeGx() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportType1() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportType42() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportWinFonts() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtTermAliases() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontSelectGlyphsFt() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontSelectLibraryFt() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontSelectRendererFt() {
|
||||
LOG_ERROR(Lib_FontFt, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterlibSceFontFt(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("e60aorDdpB8", "libSceFontFt", 1, "libSceFontFt", sceFontFtInitAliases);
|
||||
LIB_FUNCTION("BxcmiMc3UaA", "libSceFontFt", 1, "libSceFontFt", sceFontFtSetAliasFont);
|
||||
LIB_FUNCTION("MEWjebIzDEI", "libSceFontFt", 1, "libSceFontFt", sceFontFtSetAliasPath);
|
||||
LIB_FUNCTION("ZcQL0iSjvFw", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportBdf);
|
||||
LIB_FUNCTION("LADHEyFTxRQ", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportCid);
|
||||
LIB_FUNCTION("+jqQjsancTs", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportFontFormats);
|
||||
LIB_FUNCTION("oakL15-mBtc", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportOpenType);
|
||||
LIB_FUNCTION("dcQeaDr8UJc", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportOpenTypeOtf);
|
||||
LIB_FUNCTION("2KXS-HkZT3c", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportOpenTypeTtf);
|
||||
LIB_FUNCTION("H0mJnhKwV-s", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportPcf);
|
||||
LIB_FUNCTION("S2mw3sYplAI", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportPfr);
|
||||
LIB_FUNCTION("+ehNXJPUyhk", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportSystemFonts);
|
||||
LIB_FUNCTION("4BAhDLdrzUI", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportTrueType);
|
||||
LIB_FUNCTION("Utlzbdf+g9o", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportTrueTypeGx);
|
||||
LIB_FUNCTION("nAfQ6qaL1fU", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportType1);
|
||||
LIB_FUNCTION("X9+pzrGtBus", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportType42);
|
||||
LIB_FUNCTION("w0hI3xsK-hc", "libSceFontFt", 1, "libSceFontFt", sceFontFtSupportWinFonts);
|
||||
LIB_FUNCTION("w5sfH9r8ZJ4", "libSceFontFt", 1, "libSceFontFt", sceFontFtTermAliases);
|
||||
LIB_FUNCTION("ojW+VKl4Ehs", "libSceFontFt", 1, "libSceFontFt", sceFontSelectGlyphsFt);
|
||||
LIB_FUNCTION("oM+XCzVG3oM", "libSceFontFt", 1, "libSceFontFt", sceFontSelectLibraryFt);
|
||||
LIB_FUNCTION("Xx974EW-QFY", "libSceFontFt", 1, "libSceFontFt", sceFontSelectRendererFt);
|
||||
};
|
||||
|
||||
} // namespace Libraries::FontFt
|
||||
37
src/core/libraries/font/fontft.h
Normal file
37
src/core/libraries/font/fontft.h
Normal file
@ -0,0 +1,37 @@
|
||||
// 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::FontFt {
|
||||
|
||||
s32 PS4_SYSV_ABI sceFontFtInitAliases();
|
||||
s32 PS4_SYSV_ABI sceFontFtSetAliasFont();
|
||||
s32 PS4_SYSV_ABI sceFontFtSetAliasPath();
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportBdf();
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportCid();
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportFontFormats();
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportOpenType();
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportOpenTypeOtf();
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportOpenTypeTtf();
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportPcf();
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportPfr();
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportSystemFonts();
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportTrueType();
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportTrueTypeGx();
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportType1();
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportType42();
|
||||
s32 PS4_SYSV_ABI sceFontFtSupportWinFonts();
|
||||
s32 PS4_SYSV_ABI sceFontFtTermAliases();
|
||||
s32 PS4_SYSV_ABI sceFontSelectGlyphsFt();
|
||||
s32 PS4_SYSV_ABI sceFontSelectLibraryFt();
|
||||
s32 PS4_SYSV_ABI sceFontSelectRendererFt();
|
||||
|
||||
void RegisterlibSceFontFt(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::FontFt
|
||||
@ -140,7 +140,7 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) {
|
||||
return -1;
|
||||
}
|
||||
// Create a file if it doesn't exist
|
||||
Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write);
|
||||
Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Create);
|
||||
}
|
||||
} else if (!exists) {
|
||||
// If we're not creating a file, and it doesn't exist, return ENOENT
|
||||
@ -205,22 +205,30 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) {
|
||||
}
|
||||
|
||||
if (read) {
|
||||
// Read only
|
||||
// Open exclusively for reading
|
||||
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read);
|
||||
} else if (read_only) {
|
||||
// Can't open files with write/read-write access in a read only directory
|
||||
h->DeleteHandle(handle);
|
||||
*__Error() = POSIX_EROFS;
|
||||
return -1;
|
||||
} else if (append) {
|
||||
// Append can be specified with rdwr or write, but we treat it as a separate mode.
|
||||
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append);
|
||||
} else if (write) {
|
||||
// Write only
|
||||
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write);
|
||||
if (append) {
|
||||
// Open exclusively for appending
|
||||
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append);
|
||||
} else {
|
||||
// Open exclusively for writing
|
||||
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write);
|
||||
}
|
||||
} else if (rdwr) {
|
||||
// Read and write
|
||||
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite);
|
||||
if (append) {
|
||||
// Open for reading and appending
|
||||
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadAppend);
|
||||
} else {
|
||||
// Open for reading and writing
|
||||
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,6 +362,12 @@ s64 PS4_SYSV_ABI readv(s32 fd, const OrbisKernelIovec* iov, s32 iovcnt) {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (file->f.IsWriteOnly()) {
|
||||
*__Error() = POSIX_EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
s64 total_read = 0;
|
||||
for (s32 i = 0; i < iovcnt; i++) {
|
||||
total_read += ReadFile(file->f, iov[i].iov_base, iov[i].iov_len);
|
||||
@ -509,6 +523,12 @@ s64 PS4_SYSV_ABI read(s32 fd, void* buf, u64 nbytes) {
|
||||
// Socket functions handle errnos internally.
|
||||
return file->socket->ReceivePacket(buf, nbytes, 0, nullptr, 0);
|
||||
}
|
||||
|
||||
if (file->f.IsWriteOnly()) {
|
||||
*__Error() = POSIX_EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ReadFile(file->f, buf, nbytes);
|
||||
}
|
||||
|
||||
@ -620,17 +640,29 @@ s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) {
|
||||
*__Error() = POSIX_ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// get the difference between file clock and system clock
|
||||
const auto now_sys = std::chrono::system_clock::now();
|
||||
const auto now_file = std::filesystem::file_time_type::clock::now();
|
||||
// calculate the file modified time
|
||||
const auto mtime = std::filesystem::last_write_time(path_name);
|
||||
const auto mtimestamp = now_sys + (mtime - now_file);
|
||||
|
||||
if (std::filesystem::is_directory(path_name)) {
|
||||
sb->st_mode = 0000777u | 0040000u;
|
||||
sb->st_size = 65536;
|
||||
sb->st_blksize = 65536;
|
||||
sb->st_blocks = 128;
|
||||
sb->st_mtim.tv_sec =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(mtimestamp.time_since_epoch()).count();
|
||||
// TODO incomplete
|
||||
} else {
|
||||
sb->st_mode = 0000777u | 0100000u;
|
||||
sb->st_size = static_cast<s64>(std::filesystem::file_size(path_name));
|
||||
sb->st_blksize = 512;
|
||||
sb->st_blocks = (sb->st_size + 511) / 512;
|
||||
sb->st_mtim.tv_sec =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(mtimestamp.time_since_epoch()).count();
|
||||
// TODO incomplete
|
||||
}
|
||||
|
||||
@ -801,11 +833,7 @@ s32 PS4_SYSV_ABI posix_rename(const char* from, const char* to) {
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
auto file = h->GetFile(src_path);
|
||||
if (file) {
|
||||
// We need to force ReadWrite if the file had Write access before
|
||||
// Otherwise f.Open will clear the file contents.
|
||||
auto access_mode = file->f.GetAccessMode() == Common::FS::FileAccessMode::Write
|
||||
? Common::FS::FileAccessMode::ReadWrite
|
||||
: file->f.GetAccessMode();
|
||||
auto access_mode = file->f.GetAccessMode();
|
||||
file->f.Close();
|
||||
std::filesystem::remove(src_path);
|
||||
file->f.Open(dst_path, access_mode);
|
||||
@ -855,6 +883,11 @@ s64 PS4_SYSV_ABI posix_preadv(s32 fd, OrbisKernelIovec* iov, s32 iovcnt, s64 off
|
||||
return result;
|
||||
}
|
||||
|
||||
if (file->f.IsWriteOnly()) {
|
||||
*__Error() = POSIX_EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const s64 pos = file->f.Tell();
|
||||
SCOPE_EXIT {
|
||||
file->f.Seek(pos);
|
||||
|
||||
@ -236,15 +236,24 @@ s32 PS4_SYSV_ABI sceKernelSetGPO() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelGetAllowedSdkVersionOnSystem(s32* ver) {
|
||||
if (ver == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
// Returns the highest game SDK version this PS4 allows.
|
||||
*ver = CURRENT_FIRMWARE_VERSION | 0xfff;
|
||||
LOG_INFO(Lib_Kernel, "called, returned sw version: {}", *ver);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelGetSystemSwVersion(SwVersionStruct* ret) {
|
||||
if (ret == nullptr) {
|
||||
return ORBIS_OK; // but why?
|
||||
return ORBIS_OK;
|
||||
}
|
||||
ASSERT(ret->struct_size == 40);
|
||||
u32 fake_fw = Common::ElfInfo::Instance().RawFirmwareVer();
|
||||
u32 fake_fw = CURRENT_FIRMWARE_VERSION;
|
||||
ret->hex_representation = fake_fw;
|
||||
std::snprintf(ret->text_representation, 28, "%2x.%03x.%03x", fake_fw >> 0x18,
|
||||
fake_fw >> 0xc & 0xfff, fake_fw & 0xfff); // why %2x?
|
||||
fake_fw >> 0xc & 0xfff, fake_fw & 0xfff);
|
||||
LOG_INFO(Lib_Kernel, "called, returned sw version: {}", ret->text_representation);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -257,9 +266,13 @@ const char** PS4_SYSV_ABI getargv() {
|
||||
return entry_params.argv;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI get_authinfo(int pid, AuthInfoData* p2) {
|
||||
s32 PS4_SYSV_ABI get_authinfo(s32 pid, AuthInfoData* p2) {
|
||||
LOG_WARNING(Lib_Kernel, "(STUBBED) called, pid: {}", pid);
|
||||
if ((pid != 0) && (pid != GLOBAL_PID)) {
|
||||
if (p2 == nullptr) {
|
||||
*Kernel::__Error() = POSIX_EPERM;
|
||||
return -1;
|
||||
}
|
||||
if (pid != 0 && pid != GLOBAL_PID) {
|
||||
*Kernel::__Error() = POSIX_ESRCH;
|
||||
return -1;
|
||||
}
|
||||
@ -269,6 +282,22 @@ s32 PS4_SYSV_ABI get_authinfo(int pid, AuthInfoData* p2) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelGetAppInfo(s32 pid, OrbisKernelAppInfo* app_info) {
|
||||
LOG_WARNING(Lib_Kernel, "(STUBBED) called, pid: {}", pid);
|
||||
if (pid != GLOBAL_PID) {
|
||||
return ORBIS_KERNEL_ERROR_EPERM;
|
||||
}
|
||||
if (app_info == nullptr) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
auto& game_info = Common::ElfInfo::Instance();
|
||||
*app_info = {};
|
||||
app_info->has_param_sfo = 1;
|
||||
strncpy(app_info->cusa_name, game_info.GameSerial().data(), 10);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
service_thread = std::jthread{KernelServiceThread};
|
||||
|
||||
@ -285,8 +314,10 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
|
||||
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", &g_stack_chk_guard);
|
||||
LIB_FUNCTION("D4yla3vx4tY", "libkernel", 1, "libkernel", sceKernelError);
|
||||
LIB_FUNCTION("YeU23Szo3BM", "libkernel", 1, "libkernel", sceKernelGetAllowedSdkVersionOnSystem);
|
||||
LIB_FUNCTION("Mv1zUObHvXI", "libkernel", 1, "libkernel", sceKernelGetSystemSwVersion);
|
||||
LIB_FUNCTION("igMefp4SAv0", "libkernel", 1, "libkernel", get_authinfo);
|
||||
LIB_FUNCTION("G-MYv5erXaU", "libkernel", 1, "libkernel", sceKernelGetAppInfo);
|
||||
LIB_FUNCTION("PfccT7qURYE", "libkernel", 1, "libkernel", kernel_ioctl);
|
||||
LIB_FUNCTION("wW+k21cmbwQ", "libkernel", 1, "libkernel", kernel_ioctl);
|
||||
LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", sceKernelGetFsSandboxRandomWord);
|
||||
|
||||
@ -36,6 +36,8 @@ struct OrbisWrapperImpl<PS4_SYSV_ABI R (*)(Args...), f> {
|
||||
|
||||
#define ORBIS(func) (Libraries::Kernel::OrbisWrapperImpl<decltype(&(func)), func>::wrap)
|
||||
|
||||
#define CURRENT_FIRMWARE_VERSION 0x13020011
|
||||
|
||||
s32* PS4_SYSV_ABI __Error();
|
||||
|
||||
struct SwVersionStruct {
|
||||
@ -51,6 +53,30 @@ struct AuthInfoData {
|
||||
u64 ucred[8];
|
||||
};
|
||||
|
||||
struct OrbisKernelTitleWorkaround {
|
||||
s32 version;
|
||||
s32 align;
|
||||
u64 ids[2];
|
||||
};
|
||||
|
||||
struct OrbisKernelAppInfo {
|
||||
s32 app_id;
|
||||
s32 mmap_flags;
|
||||
s32 attribute_exe;
|
||||
s32 attribute2;
|
||||
char cusa_name[10];
|
||||
u8 debug_level;
|
||||
u8 slv_flags;
|
||||
u8 mini_app_dmem_flags;
|
||||
u8 render_mode;
|
||||
u8 mdbg_out;
|
||||
u8 required_hdcp_type;
|
||||
u64 preload_prx_flags;
|
||||
s32 attribute1;
|
||||
s32 has_param_sfo;
|
||||
OrbisKernelTitleWorkaround title_workaround;
|
||||
};
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
||||
@ -21,8 +21,22 @@ s32 PS4_SYSV_ABI sceKernelIsNeoMode() {
|
||||
Common::ElfInfo::Instance().GetPSFAttributes().support_neo_mode;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelHasNeoMode() {
|
||||
return Config::isNeoModeConsole();
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelGetMainSocId() {
|
||||
// These hardcoded values are based on hardware observations.
|
||||
// Different models of PS4/PS4 Pro likely return slightly different values.
|
||||
LOG_DEBUG(Lib_Kernel, "called");
|
||||
if (Config::isNeoModeConsole()) {
|
||||
return 0x740f30;
|
||||
}
|
||||
return 0x710f10;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(s32* ver) {
|
||||
s32 version = Common::ElfInfo::Instance().RawFirmwareVer();
|
||||
s32 version = Common::ElfInfo::Instance().CompiledSdkVer();
|
||||
*ver = version;
|
||||
return (version >= 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
@ -31,6 +45,11 @@ s32 PS4_SYSV_ABI sceKernelGetCpumode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelGetCurrentCpu() {
|
||||
LOG_DEBUG(Lib_Kernel, "called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* PS4_SYSV_ABI sceKernelGetProcParam() {
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
return linker->GetProcParam();
|
||||
@ -208,7 +227,10 @@ void RegisterProcess(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("xeu-pV8wkKs", "libkernel", 1, "libkernel", sceKernelIsInSandbox);
|
||||
LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", sceKernelGetCompiledSdkVersion);
|
||||
LIB_FUNCTION("WslcK1FQcGI", "libkernel", 1, "libkernel", sceKernelIsNeoMode);
|
||||
LIB_FUNCTION("rNRtm1uioyY", "libkernel", 1, "libkernel", sceKernelHasNeoMode);
|
||||
LIB_FUNCTION("0vTn5IDMU9A", "libkernel", 1, "libkernel", sceKernelGetMainSocId);
|
||||
LIB_FUNCTION("VOx8NGmHXTs", "libkernel", 1, "libkernel", sceKernelGetCpumode);
|
||||
LIB_FUNCTION("g0VTBxfJyu0", "libkernel", 1, "libkernel", sceKernelGetCurrentCpu);
|
||||
LIB_FUNCTION("959qrazPIrg", "libkernel", 1, "libkernel", sceKernelGetProcParam);
|
||||
LIB_FUNCTION("wzvqT4UqKX8", "libkernel", 1, "libkernel", sceKernelLoadStartModule);
|
||||
LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", sceKernelDlsym);
|
||||
|
||||
@ -354,6 +354,8 @@ void RegisterCond(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("Op8TBGY5KHg", "libkernel", 1, "libkernel", posix_pthread_cond_wait);
|
||||
LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", posix_pthread_cond_broadcast);
|
||||
LIB_FUNCTION("2MOy+rUfuhQ", "libkernel", 1, "libkernel", posix_pthread_cond_signal);
|
||||
LIB_FUNCTION("RXXqi4CtF8w", "libkernel", 1, "libkernel", posix_pthread_cond_destroy);
|
||||
LIB_FUNCTION("27bAgiJmOh0", "libkernel", 1, "libkernel", posix_pthread_cond_timedwait);
|
||||
LIB_FUNCTION("mKoTx03HRWA", "libkernel", 1, "libkernel", posix_pthread_condattr_init);
|
||||
LIB_FUNCTION("dJcuQVn6-Iw", "libkernel", 1, "libkernel", posix_pthread_condattr_destroy);
|
||||
|
||||
|
||||
@ -442,6 +442,8 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("ltCfaGr2JGE", "libkernel", 1, "libkernel", posix_pthread_mutex_destroy);
|
||||
LIB_FUNCTION("dQHWEsJtoE4", "libkernel", 1, "libkernel", posix_pthread_mutexattr_init);
|
||||
LIB_FUNCTION("mDmgMOGVUqg", "libkernel", 1, "libkernel", posix_pthread_mutexattr_settype);
|
||||
LIB_FUNCTION("HF7lK46xzjY", "libkernel", 1, "libkernel", posix_pthread_mutexattr_destroy);
|
||||
LIB_FUNCTION("K-jXhbt2gn4", "libkernel", 1, "libkernel", posix_pthread_mutex_trylock);
|
||||
|
||||
// Orbis
|
||||
LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", ORBIS(scePthreadMutexInit));
|
||||
|
||||
@ -663,6 +663,9 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", posix_pthread_once);
|
||||
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", posix_pthread_self);
|
||||
LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", posix_pthread_create);
|
||||
LIB_FUNCTION("+U1R4WtXvoc", "libkernel", 1, "libkernel", posix_pthread_detach);
|
||||
LIB_FUNCTION("7Xl257M4VNI", "libkernel", 1, "libkernel", posix_pthread_equal);
|
||||
LIB_FUNCTION("h9CcP3J0oVM", "libkernel", 1, "libkernel", posix_pthread_join);
|
||||
LIB_FUNCTION("Jb2uGFMr688", "libkernel", 1, "libkernel", posix_pthread_getaffinity_np);
|
||||
LIB_FUNCTION("5KWrg7-ZqvE", "libkernel", 1, "libkernel", posix_pthread_setaffinity_np);
|
||||
LIB_FUNCTION("3eqs37G74-s", "libkernel", 1, "libkernel", posix_pthread_getthreadid_np);
|
||||
|
||||
@ -9,6 +9,35 @@
|
||||
|
||||
namespace Libraries::Http {
|
||||
|
||||
static bool g_isHttpInitialized = true; // TODO temp always inited
|
||||
|
||||
void NormalizeAndAppendPath(char* dest, char* src) {
|
||||
char* lastSlash;
|
||||
u64 length;
|
||||
|
||||
lastSlash = strrchr(dest, '/');
|
||||
if (lastSlash == NULL) {
|
||||
length = strlen(dest);
|
||||
dest[length] = '/';
|
||||
dest[length + 1] = '\0';
|
||||
} else {
|
||||
lastSlash[1] = '\0';
|
||||
}
|
||||
if (*src == '/') {
|
||||
dest[0] = '\0';
|
||||
}
|
||||
length = strnlen(dest, 0x3fff);
|
||||
strncat(dest, src, 0x3fff - length);
|
||||
return;
|
||||
}
|
||||
|
||||
int HttpRequestInternal_Acquire(HttpRequestInternal** outRequest, u32 requestId) {
|
||||
return 0; // TODO dummy
|
||||
}
|
||||
int HttpRequestInternal_Release(HttpRequestInternal* request) {
|
||||
return 0; // TODO dummy
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpAbortRequest() {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
@ -34,8 +63,9 @@ int PS4_SYSV_ABI sceHttpAddQuery() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpAddRequestHeader() {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||
int PS4_SYSV_ABI sceHttpAddRequestHeader(int id, const char* name, const char* value, s32 mode) {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called id= {} name = {} value = {} mode = {}", id,
|
||||
std::string(name), std::string(value), mode);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -84,8 +114,9 @@ int PS4_SYSV_ABI sceHttpCreateConnection() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpCreateConnectionWithURL() {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||
int PS4_SYSV_ABI sceHttpCreateConnectionWithURL(int tmplId, const char* url, bool enableKeepalive) {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called tmpid = {} url = {} enableKeepalive = {}", tmplId,
|
||||
std::string(url), enableKeepalive ? 1 : 0);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -104,8 +135,10 @@ int PS4_SYSV_ABI sceHttpCreateRequest2() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpCreateRequestWithURL() {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||
int PS4_SYSV_ABI sceHttpCreateRequestWithURL(int connId, s32 method, const char* url,
|
||||
u64 contentLength) {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called connId = {} method = {} url={} contentLength={}", connId,
|
||||
method, url, contentLength);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -184,7 +217,7 @@ int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpGetAllResponseHeaders() {
|
||||
int PS4_SYSV_ABI sceHttpGetAllResponseHeaders(int reqId, char** header, u64* headerSize) {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||
return ORBIS_FAIL;
|
||||
}
|
||||
@ -254,12 +287,42 @@ int PS4_SYSV_ABI sceHttpGetResponseContentLength() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpGetStatusCode() {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||
int PS4_SYSV_ABI sceHttpGetStatusCode(int reqId, int* statusCode) {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called reqId = {}", reqId);
|
||||
#if 0
|
||||
if (!g_isHttpInitialized)
|
||||
return ORBIS_HTTP_ERROR_BEFORE_INIT;
|
||||
|
||||
if (statusCode == nullptr)
|
||||
return ORBIS_HTTP_ERROR_INVALID_VALUE;
|
||||
|
||||
int ret = 0;
|
||||
// Lookup HttpRequestInternal by reqId
|
||||
HttpRequestInternal* request = nullptr;
|
||||
ret = HttpRequestInternal_Acquire(&request, reqId);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
request->m_mutex.lock();
|
||||
if (request->state > 0x11) {
|
||||
if (request->state == 0x16) {
|
||||
ret = request->errorCode;
|
||||
} else {
|
||||
*statusCode = request->httpStatusCode;
|
||||
ret = 0;
|
||||
}
|
||||
} else {
|
||||
ret = ORBIS_HTTP_ERROR_BEFORE_SEND;
|
||||
}
|
||||
request->m_mutex.unlock();
|
||||
HttpRequestInternal_Release(request);
|
||||
|
||||
return ret;
|
||||
#else
|
||||
return ORBIS_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, std::size_t poolSize) {
|
||||
int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, u64 poolSize) {
|
||||
LOG_ERROR(Lib_Http, "(DUMMY) called libnetMemId = {} libsslCtxId = {} poolSize = {}",
|
||||
libnetMemId, libsslCtxId, poolSize);
|
||||
// return a value >1
|
||||
@ -267,14 +330,104 @@ int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, std::size_t poolS
|
||||
return ++id;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpParseResponseHeader() {
|
||||
int PS4_SYSV_ABI sceHttpParseResponseHeader(const char* header, u64 headerLen, const char* fieldStr,
|
||||
const char** fieldValue, u64* valueLen) {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpParseStatusLine() {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
int PS4_SYSV_ABI sceHttpParseStatusLine(const char* statusLine, u64 lineLen, int32_t* httpMajorVer,
|
||||
int32_t* httpMinorVer, int32_t* responseCode,
|
||||
const char** reasonPhrase, u64* phraseLen) {
|
||||
if (!statusLine) {
|
||||
LOG_ERROR(Lib_Http, "Invalid response");
|
||||
return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE;
|
||||
}
|
||||
if (!httpMajorVer || !httpMinorVer || !responseCode || !reasonPhrase || !phraseLen) {
|
||||
LOG_ERROR(Lib_Http, "Invalid value");
|
||||
return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_VALUE;
|
||||
}
|
||||
*httpMajorVer = 0;
|
||||
*httpMinorVer = 0;
|
||||
if (lineLen < 8) {
|
||||
LOG_ERROR(Lib_Http, "Linelen is smaller than 8");
|
||||
return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE;
|
||||
}
|
||||
if (strncmp(statusLine, "HTTP/", 5) != 0) {
|
||||
LOG_ERROR(Lib_Http, "statusLine doesn't start with HTTP/");
|
||||
return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
u64 index = 5;
|
||||
|
||||
if (!isdigit(statusLine[index])) {
|
||||
LOG_ERROR(Lib_Http, "Invalid response");
|
||||
|
||||
return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
while (isdigit(statusLine[index])) {
|
||||
*httpMajorVer = *httpMajorVer * 10 + (statusLine[index] - '0');
|
||||
index++;
|
||||
}
|
||||
|
||||
if (statusLine[index] != '.') {
|
||||
LOG_ERROR(Lib_Http, "Invalid response");
|
||||
return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE;
|
||||
}
|
||||
index++;
|
||||
|
||||
if (!isdigit(statusLine[index])) {
|
||||
LOG_ERROR(Lib_Http, "Invalid response");
|
||||
return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
while (isdigit(statusLine[index])) {
|
||||
*httpMinorVer = *httpMinorVer * 10 + (statusLine[index] - '0');
|
||||
index++;
|
||||
}
|
||||
|
||||
if (statusLine[index] != ' ') {
|
||||
LOG_ERROR(Lib_Http, "Invalid response");
|
||||
return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE;
|
||||
}
|
||||
index++;
|
||||
|
||||
// Validate and parse the 3-digit HTTP response code
|
||||
if (lineLen - index < 3 || !isdigit(statusLine[index]) || !isdigit(statusLine[index + 1]) ||
|
||||
!isdigit(statusLine[index + 2])) {
|
||||
LOG_ERROR(Lib_Http, "Invalid response");
|
||||
return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
*responseCode = (statusLine[index] - '0') * 100 + (statusLine[index + 1] - '0') * 10 +
|
||||
(statusLine[index + 2] - '0');
|
||||
index += 3;
|
||||
|
||||
if (statusLine[index] != ' ') {
|
||||
LOG_ERROR(Lib_Http, "Invalid response");
|
||||
return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE;
|
||||
}
|
||||
index++;
|
||||
|
||||
// Set the reason phrase start position
|
||||
*reasonPhrase = &statusLine[index];
|
||||
u64 phraseStart = index;
|
||||
|
||||
while (index < lineLen && statusLine[index] != '\n') {
|
||||
index++;
|
||||
}
|
||||
|
||||
// Determine the length of the reason phrase, excluding trailing \r if present
|
||||
if (index == phraseStart) {
|
||||
*phraseLen = 0;
|
||||
} else {
|
||||
*phraseLen =
|
||||
(statusLine[index - 1] == '\r') ? (index - phraseStart - 1) : (index - phraseStart);
|
||||
}
|
||||
|
||||
// Return the number of bytes processed
|
||||
return index + 1;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpReadData() {
|
||||
@ -317,8 +470,8 @@ int PS4_SYSV_ABI sceHttpsEnableOptionPrivate() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpSendRequest() {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||
int PS4_SYSV_ABI sceHttpSendRequest(int reqId, const void* postData, u64 size) {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called reqId = {} size = {}", reqId, size);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -548,7 +701,8 @@ int PS4_SYSV_ABI sceHttpUnsetEpoll() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpUriBuild() {
|
||||
int PS4_SYSV_ABI sceHttpUriBuild(char* out, u64* require, u64 prepare,
|
||||
const OrbisHttpUriElement* srcElement, u32 option) {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -563,13 +717,97 @@ int PS4_SYSV_ABI sceHttpUriEscape() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpUriMerge() {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
int PS4_SYSV_ABI sceHttpUriMerge(char* mergedUrl, char* url, char* relativeUri, u64* require,
|
||||
u64 prepare, u32 option) {
|
||||
u64 requiredLength;
|
||||
int returnValue;
|
||||
u64 baseUrlLength;
|
||||
u64 relativeUriLength;
|
||||
u64 totalLength;
|
||||
u64 combinedLength;
|
||||
int parseResult;
|
||||
u64 localSizeRelativeUri;
|
||||
u64 localSizeBaseUrl;
|
||||
OrbisHttpUriElement parsedUriElement;
|
||||
|
||||
if (option != 0 || url == NULL || relativeUri == NULL) {
|
||||
LOG_ERROR(Lib_Http, "Invalid value");
|
||||
return ORBIS_HTTP_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
returnValue = sceHttpUriParse(NULL, url, NULL, &localSizeBaseUrl, 0);
|
||||
if (returnValue < 0) {
|
||||
LOG_ERROR(Lib_Http, "returning {:#x}", returnValue);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
returnValue = sceHttpUriParse(NULL, relativeUri, NULL, &localSizeRelativeUri, 0);
|
||||
if (returnValue < 0) {
|
||||
LOG_ERROR(Lib_Http, "returning {:#x}", returnValue);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
baseUrlLength = strnlen(url, 0x3fff);
|
||||
relativeUriLength = strnlen(relativeUri, 0x3fff);
|
||||
requiredLength = localSizeBaseUrl + 2 + (relativeUriLength + baseUrlLength) * 2;
|
||||
|
||||
if (require) {
|
||||
*require = requiredLength;
|
||||
}
|
||||
|
||||
if (mergedUrl == NULL) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
if (prepare < requiredLength) {
|
||||
LOG_ERROR(Lib_Http, "Error Out of memory");
|
||||
return ORBIS_HTTP_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
totalLength = strnlen(url, 0x3fff);
|
||||
baseUrlLength = strnlen(relativeUri, 0x3fff);
|
||||
combinedLength = totalLength + 1 + baseUrlLength;
|
||||
relativeUriLength = prepare - combinedLength;
|
||||
|
||||
returnValue =
|
||||
sceHttpUriParse(&parsedUriElement, relativeUri, mergedUrl + totalLength + baseUrlLength + 1,
|
||||
&localSizeRelativeUri, relativeUriLength);
|
||||
if (returnValue < 0) {
|
||||
LOG_ERROR(Lib_Http, "returning {:#x}", returnValue);
|
||||
return returnValue;
|
||||
}
|
||||
if (parsedUriElement.scheme == NULL) {
|
||||
strncpy(mergedUrl, relativeUri, requiredLength);
|
||||
if (require) {
|
||||
*require = strnlen(relativeUri, 0x3fff) + 1;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
returnValue =
|
||||
sceHttpUriParse(&parsedUriElement, url, mergedUrl + totalLength + baseUrlLength + 1,
|
||||
&localSizeBaseUrl, relativeUriLength);
|
||||
if (returnValue < 0) {
|
||||
LOG_ERROR(Lib_Http, "returning {:#x}", returnValue);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
combinedLength += localSizeBaseUrl;
|
||||
strncpy(mergedUrl + combinedLength, parsedUriElement.path, prepare - combinedLength);
|
||||
NormalizeAndAppendPath(mergedUrl + combinedLength, relativeUri);
|
||||
|
||||
returnValue = sceHttpUriBuild(mergedUrl, 0, ~(baseUrlLength + totalLength) + prepare,
|
||||
&parsedUriElement, 0x3f);
|
||||
if (returnValue >= 0) {
|
||||
return ORBIS_OK;
|
||||
} else {
|
||||
LOG_ERROR(Lib_Http, "returning {:#x}", returnValue);
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool,
|
||||
size_t* require, size_t prepare) {
|
||||
u64* require, u64 prepare) {
|
||||
LOG_INFO(Lib_Http, "srcUri = {}", std::string(srcUri));
|
||||
if (!srcUri) {
|
||||
LOG_ERROR(Lib_Http, "invalid url");
|
||||
@ -586,10 +824,10 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
||||
}
|
||||
|
||||
// Track the total required buffer size
|
||||
size_t requiredSize = 0;
|
||||
u64 requiredSize = 0;
|
||||
|
||||
// Parse the scheme (e.g., "http:", "https:", "file:")
|
||||
size_t schemeLength = 0;
|
||||
u64 schemeLength = 0;
|
||||
while (srcUri[schemeLength] && srcUri[schemeLength] != ':') {
|
||||
if (!isalnum(srcUri[schemeLength])) {
|
||||
LOG_ERROR(Lib_Http, "invalid url");
|
||||
@ -611,7 +849,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
||||
requiredSize += schemeLength + 1;
|
||||
|
||||
// Move past the scheme and ':' character
|
||||
size_t offset = schemeLength + 1;
|
||||
u64 offset = schemeLength + 1;
|
||||
|
||||
// Check if "//" appears after the scheme
|
||||
if (strncmp(srcUri + offset, "//", 2) == 0) {
|
||||
@ -638,7 +876,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
||||
|
||||
// Parse the path (everything after the slashes)
|
||||
char* pathStart = (char*)srcUri + offset;
|
||||
size_t pathLength = 0;
|
||||
u64 pathLength = 0;
|
||||
while (pathStart[pathLength] && pathStart[pathLength] != '?' &&
|
||||
pathStart[pathLength] != '#') {
|
||||
pathLength++;
|
||||
@ -689,7 +927,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
||||
hostStart++;
|
||||
}
|
||||
|
||||
size_t hostLength = 0;
|
||||
u64 hostLength = 0;
|
||||
while (hostStart[hostLength] && hostStart[hostLength] != '/' &&
|
||||
hostStart[hostLength] != '?' && hostStart[hostLength] != ':') {
|
||||
hostLength++;
|
||||
@ -714,7 +952,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
||||
// Parse the port (if present)
|
||||
if (hostStart[hostLength] == ':') {
|
||||
char* portStart = hostStart + hostLength + 1;
|
||||
size_t portLength = 0;
|
||||
u64 portLength = 0;
|
||||
while (portStart[portLength] && isdigit(portStart[portLength])) {
|
||||
portLength++;
|
||||
}
|
||||
@ -754,7 +992,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
||||
// Parse the path (if present)
|
||||
if (srcUri[offset] == '/') {
|
||||
char* pathStart = (char*)srcUri + offset;
|
||||
size_t pathLength = 0;
|
||||
u64 pathLength = 0;
|
||||
while (pathStart[pathLength] && pathStart[pathLength] != '?' &&
|
||||
pathStart[pathLength] != '#') {
|
||||
pathLength++;
|
||||
@ -780,7 +1018,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
||||
// Parse the query (if present)
|
||||
if (srcUri[offset] == '?') {
|
||||
char* queryStart = (char*)srcUri + offset + 1;
|
||||
size_t queryLength = 0;
|
||||
u64 queryLength = 0;
|
||||
while (queryStart[queryLength] && queryStart[queryLength] != '#') {
|
||||
queryLength++;
|
||||
}
|
||||
@ -805,7 +1043,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
||||
// Parse the fragment (if present)
|
||||
if (srcUri[offset] == '#') {
|
||||
char* fragmentStart = (char*)srcUri + offset + 1;
|
||||
size_t fragmentLength = 0;
|
||||
u64 fragmentLength = 0;
|
||||
while (fragmentStart[fragmentLength]) {
|
||||
fragmentLength++;
|
||||
}
|
||||
@ -833,12 +1071,12 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, size_t srcSize) {
|
||||
int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, u64 srcSize) {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceHttpUriUnescape(char* out, size_t* require, size_t prepare, const char* in) {
|
||||
int PS4_SYSV_ABI sceHttpUriUnescape(char* out, u64* require, u64 prepare, const char* in) {
|
||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "common/types.h"
|
||||
#include "core/libraries/network/ssl.h"
|
||||
|
||||
@ -25,6 +26,12 @@ struct OrbisHttpUriElement {
|
||||
u8 reserved[10];
|
||||
};
|
||||
|
||||
struct HttpRequestInternal {
|
||||
int state; // +0x20
|
||||
int errorCode; // +0x28
|
||||
int httpStatusCode; // +0x20C
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
using OrbisHttpsCaList = Libraries::Ssl::OrbisSslCaList;
|
||||
|
||||
int PS4_SYSV_ABI sceHttpAbortRequest();
|
||||
@ -32,7 +39,7 @@ int PS4_SYSV_ABI sceHttpAbortRequestForce();
|
||||
int PS4_SYSV_ABI sceHttpAbortWaitRequest();
|
||||
int PS4_SYSV_ABI sceHttpAddCookie();
|
||||
int PS4_SYSV_ABI sceHttpAddQuery();
|
||||
int PS4_SYSV_ABI sceHttpAddRequestHeader();
|
||||
int PS4_SYSV_ABI sceHttpAddRequestHeader(int id, const char* name, const char* value, s32 mode);
|
||||
int PS4_SYSV_ABI sceHttpAddRequestHeaderRaw();
|
||||
int PS4_SYSV_ABI sceHttpAuthCacheExport();
|
||||
int PS4_SYSV_ABI sceHttpAuthCacheFlush();
|
||||
@ -42,11 +49,12 @@ int PS4_SYSV_ABI sceHttpCookieExport();
|
||||
int PS4_SYSV_ABI sceHttpCookieFlush();
|
||||
int PS4_SYSV_ABI sceHttpCookieImport();
|
||||
int PS4_SYSV_ABI sceHttpCreateConnection();
|
||||
int PS4_SYSV_ABI sceHttpCreateConnectionWithURL();
|
||||
int PS4_SYSV_ABI sceHttpCreateConnectionWithURL(int tmplId, const char* url, bool enableKeepalive);
|
||||
int PS4_SYSV_ABI sceHttpCreateEpoll();
|
||||
int PS4_SYSV_ABI sceHttpCreateRequest();
|
||||
int PS4_SYSV_ABI sceHttpCreateRequest2();
|
||||
int PS4_SYSV_ABI sceHttpCreateRequestWithURL();
|
||||
int PS4_SYSV_ABI sceHttpCreateRequestWithURL(int connId, s32 method, const char* url,
|
||||
u64 contentLength);
|
||||
int PS4_SYSV_ABI sceHttpCreateRequestWithURL2();
|
||||
int PS4_SYSV_ABI sceHttpCreateTemplate();
|
||||
int PS4_SYSV_ABI sceHttpDbgEnableProfile();
|
||||
@ -62,7 +70,7 @@ int PS4_SYSV_ABI sceHttpDeleteRequest();
|
||||
int PS4_SYSV_ABI sceHttpDeleteTemplate();
|
||||
int PS4_SYSV_ABI sceHttpDestroyEpoll();
|
||||
int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled();
|
||||
int PS4_SYSV_ABI sceHttpGetAllResponseHeaders();
|
||||
int PS4_SYSV_ABI sceHttpGetAllResponseHeaders(int reqId, char** header, u64* headerSize);
|
||||
int PS4_SYSV_ABI sceHttpGetAuthEnabled();
|
||||
int PS4_SYSV_ABI sceHttpGetAutoRedirect();
|
||||
int PS4_SYSV_ABI sceHttpGetConnectionStat();
|
||||
@ -76,10 +84,13 @@ int PS4_SYSV_ABI sceHttpGetMemoryPoolStats();
|
||||
int PS4_SYSV_ABI sceHttpGetNonblock();
|
||||
int PS4_SYSV_ABI sceHttpGetRegisteredCtxIds();
|
||||
int PS4_SYSV_ABI sceHttpGetResponseContentLength();
|
||||
int PS4_SYSV_ABI sceHttpGetStatusCode();
|
||||
int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, std::size_t poolSize);
|
||||
int PS4_SYSV_ABI sceHttpParseResponseHeader();
|
||||
int PS4_SYSV_ABI sceHttpParseStatusLine();
|
||||
int PS4_SYSV_ABI sceHttpGetStatusCode(int reqId, int* statusCode);
|
||||
int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, u64 poolSize);
|
||||
int PS4_SYSV_ABI sceHttpParseResponseHeader(const char* header, u64 headerLen, const char* fieldStr,
|
||||
const char** fieldValue, u64* valueLen);
|
||||
int PS4_SYSV_ABI sceHttpParseStatusLine(const char* statusLine, u64 lineLen, int32_t* httpMajorVer,
|
||||
int32_t* httpMinorVer, int32_t* responseCode,
|
||||
const char** reasonPhrase, u64* phraseLen);
|
||||
int PS4_SYSV_ABI sceHttpReadData();
|
||||
int PS4_SYSV_ABI sceHttpRedirectCacheFlush();
|
||||
int PS4_SYSV_ABI sceHttpRemoveRequestHeader();
|
||||
@ -88,7 +99,7 @@ int PS4_SYSV_ABI sceHttpsDisableOption();
|
||||
int PS4_SYSV_ABI sceHttpsDisableOptionPrivate();
|
||||
int PS4_SYSV_ABI sceHttpsEnableOption();
|
||||
int PS4_SYSV_ABI sceHttpsEnableOptionPrivate();
|
||||
int PS4_SYSV_ABI sceHttpSendRequest();
|
||||
int PS4_SYSV_ABI sceHttpSendRequest(int reqId, const void* postData, u64 size);
|
||||
int PS4_SYSV_ABI sceHttpSetAcceptEncodingGZIPEnabled();
|
||||
int PS4_SYSV_ABI sceHttpSetAuthEnabled();
|
||||
int PS4_SYSV_ABI sceHttpSetAuthInfoCallback();
|
||||
@ -134,14 +145,16 @@ int PS4_SYSV_ABI sceHttpTerm();
|
||||
int PS4_SYSV_ABI sceHttpTryGetNonblock();
|
||||
int PS4_SYSV_ABI sceHttpTrySetNonblock();
|
||||
int PS4_SYSV_ABI sceHttpUnsetEpoll();
|
||||
int PS4_SYSV_ABI sceHttpUriBuild();
|
||||
int PS4_SYSV_ABI sceHttpUriBuild(char* out, u64* require, u64 prepare,
|
||||
const OrbisHttpUriElement* srcElement, u32 option);
|
||||
int PS4_SYSV_ABI sceHttpUriCopy();
|
||||
int PS4_SYSV_ABI sceHttpUriEscape();
|
||||
int PS4_SYSV_ABI sceHttpUriMerge();
|
||||
int PS4_SYSV_ABI sceHttpUriMerge(char* mergedUrl, char* url, char* relativeUri, u64* require,
|
||||
u64 prepare, u32 option);
|
||||
int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool,
|
||||
size_t* require, size_t prepare);
|
||||
int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, size_t srcSize);
|
||||
int PS4_SYSV_ABI sceHttpUriUnescape(char* out, size_t* require, size_t prepare, const char* in);
|
||||
u64* require, u64 prepare);
|
||||
int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, u64 srcSize);
|
||||
int PS4_SYSV_ABI sceHttpUriUnescape(char* out, u64* require, u64 prepare, const char* in);
|
||||
int PS4_SYSV_ABI sceHttpWaitRequest();
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
@ -1285,7 +1285,8 @@ u16 PS4_SYSV_ABI sceNetNtohs(u16 net16) {
|
||||
|
||||
int PS4_SYSV_ABI sceNetPoolCreate(const char* name, int size, int flags) {
|
||||
LOG_ERROR(Lib_Net, "(DUMMY) name = {} size = {} flags = {} ", std::string(name), size, flags);
|
||||
return ORBIS_OK;
|
||||
static s32 id = 1;
|
||||
return id++;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetPoolDestroy() {
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
#include <SDL3/SDL_audio.h>
|
||||
#include <cmrc/cmrc.hpp>
|
||||
#include <imgui.h>
|
||||
|
||||
@ -92,59 +91,45 @@ TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::strin
|
||||
|
||||
AddLayer(this);
|
||||
|
||||
bool customsoundplayed = false;
|
||||
#ifdef ENABLE_QT_GUI
|
||||
QString musicPathWav = QString::fromStdString(CustomTrophy_Dir.string() + "/trophy.wav");
|
||||
QString musicPathMp3 = QString::fromStdString(CustomTrophy_Dir.string() + "/trophy.mp3");
|
||||
if (fs::exists(musicPathWav.toStdString())) {
|
||||
BackgroundMusicPlayer::getInstance().setVolume(100);
|
||||
BackgroundMusicPlayer::getInstance().playMusic(musicPathWav, false);
|
||||
customsoundplayed = true;
|
||||
} else if (fs::exists(musicPathMp3.toStdString())) {
|
||||
BackgroundMusicPlayer::getInstance().setVolume(100);
|
||||
BackgroundMusicPlayer::getInstance().playMusic(musicPathMp3, false);
|
||||
customsoundplayed = true;
|
||||
MIX_Init();
|
||||
mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL);
|
||||
if (!mixer) {
|
||||
LOG_ERROR(Lib_NpTrophy, "Could not initialize SDL Mixer, {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!customsoundplayed) {
|
||||
MIX_SetMasterGain(mixer, static_cast<float>(Config::getVolumeSlider() / 100.f));
|
||||
auto musicPathMp3 = CustomTrophy_Dir / "trophy.mp3";
|
||||
auto musicPathWav = CustomTrophy_Dir / "trophy.wav";
|
||||
|
||||
if (std::filesystem::exists(musicPathMp3)) {
|
||||
audio = MIX_LoadAudio(mixer, musicPathMp3.string().c_str(), false);
|
||||
} else if (std::filesystem::exists(musicPathWav)) {
|
||||
audio = MIX_LoadAudio(mixer, musicPathWav.string().c_str(), false);
|
||||
} else {
|
||||
auto soundFile = resource.open("src/images/trophy.wav");
|
||||
std::vector<u8> soundData = std::vector<u8>(soundFile.begin(), soundFile.end());
|
||||
audio =
|
||||
MIX_LoadAudio_IO(mixer, SDL_IOFromMem(soundData.data(), soundData.size()), false, true);
|
||||
// due to low volume of default sound file
|
||||
MIX_SetMasterGain(mixer, MIX_GetMasterGain(mixer) * 1.3f);
|
||||
}
|
||||
|
||||
SDL_AudioSpec spec;
|
||||
Uint8* audioBuf;
|
||||
Uint32 audioLen;
|
||||
if (!audio) {
|
||||
LOG_ERROR(Lib_NpTrophy, "Could not loud audio file, {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SDL_LoadWAV_IO(SDL_IOFromMem(soundData.data(), soundData.size()), true, &spec,
|
||||
&audioBuf, &audioLen)) {
|
||||
LOG_ERROR(Lib_NpTrophy, "Cannot load trophy sound: {}", SDL_GetError());
|
||||
SDL_free(audioBuf);
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_AudioStream* stream =
|
||||
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, nullptr, nullptr);
|
||||
if (!stream) {
|
||||
LOG_ERROR(Lib_NpTrophy, "Cannot create audio stream for trophy sound: {}",
|
||||
SDL_GetError());
|
||||
SDL_free(audioBuf);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SDL_PutAudioStreamData(stream, audioBuf, audioLen)) {
|
||||
LOG_ERROR(Lib_NpTrophy, "Cannot add trophy sound data to stream: {}", SDL_GetError());
|
||||
SDL_free(audioBuf);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set audio gain 20% higher since audio file itself is soft
|
||||
SDL_SetAudioStreamGain(stream, Config::getVolumeSlider() / 100.0f * 1.2f);
|
||||
SDL_ResumeAudioStreamDevice(stream);
|
||||
SDL_free(audioBuf);
|
||||
if (!MIX_PlayAudio(mixer, audio)) {
|
||||
LOG_ERROR(Lib_NpTrophy, "Could not play audio file, {}", SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
TrophyUI::~TrophyUI() {
|
||||
MIX_DestroyAudio(audio);
|
||||
MIX_DestroyMixer(mixer);
|
||||
MIX_Quit();
|
||||
|
||||
Finish();
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <SDL3_mixer/SDL_mixer.h>
|
||||
#include <queue>
|
||||
|
||||
#include "common/fixed_value.h"
|
||||
@ -30,6 +31,9 @@ private:
|
||||
std::string_view trophy_type;
|
||||
ImGui::RefCountedTexture trophy_icon;
|
||||
ImGui::RefCountedTexture trophy_type_icon;
|
||||
|
||||
MIX_Mixer* mixer;
|
||||
MIX_Audio* audio;
|
||||
};
|
||||
|
||||
struct TrophyInfo {
|
||||
|
||||
@ -180,7 +180,7 @@ void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_cor
|
||||
}
|
||||
|
||||
if (!ignore_corrupt && !read_only) {
|
||||
Common::FS::IOFile f(corrupt_file_path, Common::FS::FileAccessMode::Write);
|
||||
Common::FS::IOFile f(corrupt_file_path, Common::FS::FileAccessMode::Create);
|
||||
f.Close();
|
||||
}
|
||||
|
||||
|
||||
@ -59,7 +59,7 @@ void PersistMemory(u32 slot_id, bool lock) {
|
||||
while (n++ < 10) {
|
||||
try {
|
||||
IOFile f;
|
||||
int r = f.Open(memoryPath, Common::FS::FileAccessMode::Write);
|
||||
int r = f.Open(memoryPath, Common::FS::FileAccessMode::Create);
|
||||
if (f.IsOpen()) {
|
||||
f.WriteRaw<u8>(data.memory_cache.data(), data.memory_cache.size());
|
||||
f.Close();
|
||||
@ -148,7 +148,7 @@ void SetIcon(u32 slot_id, void* buf, size_t buf_size) {
|
||||
fs::copy_file(src_icon, icon_path);
|
||||
}
|
||||
} else {
|
||||
IOFile file(icon_path, Common::FS::FileAccessMode::Write);
|
||||
IOFile file(icon_path, Common::FS::FileAccessMode::Create);
|
||||
file.WriteRaw<u8>(buf, buf_size);
|
||||
file.Close();
|
||||
}
|
||||
|
||||
@ -1389,7 +1389,7 @@ Error PS4_SYSV_ABI sceSaveDataSaveIcon(const OrbisSaveDataMountPoint* mountPoint
|
||||
}
|
||||
|
||||
try {
|
||||
const Common::FS::IOFile file(path, Common::FS::FileAccessMode::Write);
|
||||
const Common::FS::IOFile file(path, Common::FS::FileAccessMode::Create);
|
||||
file.WriteRaw<u8>(icon->buf, std::min(icon->bufSize, icon->dataSize));
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
LOG_ERROR(Lib_SaveData, "Failed to load icon: {}", e.what());
|
||||
|
||||
651
src/core/libraries/usbd/emulated/dimensions.cpp
Normal file
651
src/core/libraries/usbd/emulated/dimensions.cpp
Normal file
@ -0,0 +1,651 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "dimensions.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
namespace Libraries::Usbd {
|
||||
|
||||
static constexpr std::array<u8, 16> COMMAND_KEY = {0x55, 0xFE, 0xF6, 0xB0, 0x62, 0xBF, 0x0B, 0x41,
|
||||
0xC9, 0xB3, 0x7C, 0xB4, 0x97, 0x3E, 0x29, 0x7B};
|
||||
|
||||
static constexpr std::array<u8, 17> CHAR_CONSTANT = {0xB7, 0xD5, 0xD7, 0xE6, 0xE7, 0xBA,
|
||||
0x3C, 0xA8, 0xD8, 0x75, 0x47, 0x68,
|
||||
0xCF, 0x23, 0xE9, 0xFE, 0xAA};
|
||||
|
||||
static constexpr std::array<u8, 25> PWD_CONSTANT = {
|
||||
0x28, 0x63, 0x29, 0x20, 0x43, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74,
|
||||
0x20, 0x4C, 0x45, 0x47, 0x4F, 0x20, 0x32, 0x30, 0x31, 0x34, 0xAA, 0xAA};
|
||||
|
||||
DimensionsToypad::DimensionsToypad() {}
|
||||
|
||||
void DimensionsToypad::LoadFigure(std::string file_name, u8 pad, u8 index) {
|
||||
Common::FS::IOFile file(file_name, Common::FS::FileAccessMode::ReadWrite);
|
||||
std::array<u8, 0x2D * 0x04> data;
|
||||
ASSERT(file.Read(data) == data.size());
|
||||
LoadDimensionsFigure(data, std::move(file), pad, index);
|
||||
}
|
||||
|
||||
u32 DimensionsToypad::LoadDimensionsFigure(const std::array<u8, 0x2D * 0x04>& buf,
|
||||
Common::FS::IOFile file, u8 pad, u8 index) {
|
||||
std::lock_guard lock(m_dimensions_mutex);
|
||||
|
||||
const u32 id = GetFigureId(buf);
|
||||
|
||||
DimensionsFigure& figure = GetFigureByIndex(index);
|
||||
figure.dimFile = std::move(file);
|
||||
figure.id = id;
|
||||
figure.pad = pad;
|
||||
figure.index = index + 1;
|
||||
figure.data = buf;
|
||||
// When a figure is added to the toypad, respond to the game with the pad they were added to,
|
||||
// their index, the direction (0x00 in byte 6 for added) and their UID
|
||||
std::array<u8, 32> figureChangeResponse = {0x56, 0x0b, figure.pad, 0x00, figure.index,
|
||||
0x00, buf[0], buf[1], buf[2], buf[4],
|
||||
buf[5], buf[6], buf[7]};
|
||||
figureChangeResponse[13] = GenerateChecksum(figureChangeResponse, 13);
|
||||
m_figure_added_removed_responses.push(figureChangeResponse);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void DimensionsToypad::RemoveFigure(u8 pad, u8 index, bool fullRemove) {
|
||||
std::lock_guard lock(m_dimensions_mutex);
|
||||
|
||||
DimensionsFigure& figure = GetFigureByIndex(index);
|
||||
if (figure.index == 255)
|
||||
return;
|
||||
|
||||
// When a figure is removed from the toypad, respond to the game with the pad they were removed
|
||||
// from, their index, the direction (0x01 in byte 6 for removed) and their UID
|
||||
if (fullRemove) {
|
||||
std::array<u8, 32> figureChangeResponse = {
|
||||
0x56, 0x0b, figure.pad, 0x00, figure.index,
|
||||
0x01, figure.data[0], figure.data[1], figure.data[2], figure.data[4],
|
||||
figure.data[5], figure.data[6], figure.data[7]};
|
||||
figureChangeResponse[13] = GenerateChecksum(figureChangeResponse, 13);
|
||||
m_figure_added_removed_responses.push(figureChangeResponse);
|
||||
figure.Save();
|
||||
figure.dimFile.Close();
|
||||
}
|
||||
|
||||
figure.index = 255;
|
||||
figure.pad = 255;
|
||||
figure.id = 0;
|
||||
}
|
||||
|
||||
void DimensionsToypad::MoveFigure(u8 new_pad, u8 new_index, u8 old_pad, u8 old_index) {
|
||||
if (old_index == new_index) {
|
||||
// Don't bother removing and loading again, just send response to the game
|
||||
CancelRemoveFigure(new_index);
|
||||
return;
|
||||
}
|
||||
|
||||
// When moving figures between spaces on the toypad, remove any figure from the space they are
|
||||
// moving to, then remove them from their current space, then load them to the space they are
|
||||
// moving to
|
||||
RemoveFigure(new_pad, new_index, true);
|
||||
|
||||
DimensionsFigure& figure = GetFigureByIndex(old_index);
|
||||
const std::array<u8, 0x2D * 0x04> data = figure.data;
|
||||
Common::FS::IOFile inFile = std::move(figure.dimFile);
|
||||
|
||||
RemoveFigure(old_pad, old_index, false);
|
||||
|
||||
LoadDimensionsFigure(data, std::move(inFile), new_pad, new_index);
|
||||
}
|
||||
|
||||
void DimensionsToypad::TempRemoveFigure(u8 index) {
|
||||
std::lock_guard lock(m_dimensions_mutex);
|
||||
|
||||
DimensionsFigure& figure = GetFigureByIndex(index);
|
||||
if (figure.index == 255)
|
||||
return;
|
||||
|
||||
// Send a response to the game that the figure has been "Picked up" from existing slot,
|
||||
// until either the movement is cancelled, or user chooses a space to move to
|
||||
std::array<u8, 32> figureChangeResponse = {
|
||||
0x56, 0x0b, figure.pad, 0x00, figure.index,
|
||||
0x01, figure.data[0], figure.data[1], figure.data[2], figure.data[4],
|
||||
figure.data[5], figure.data[6], figure.data[7]};
|
||||
|
||||
figureChangeResponse[13] = GenerateChecksum(figureChangeResponse, 13);
|
||||
m_figure_added_removed_responses.push(figureChangeResponse);
|
||||
}
|
||||
|
||||
void DimensionsToypad::CancelRemoveFigure(u8 index) {
|
||||
std::lock_guard lock(m_dimensions_mutex);
|
||||
|
||||
DimensionsFigure& figure = GetFigureByIndex(index);
|
||||
if (figure.index == 255)
|
||||
return;
|
||||
|
||||
// Cancel the previous movement of the figure
|
||||
std::array<u8, 32> figureChangeResponse = {
|
||||
0x56, 0x0b, figure.pad, 0x00, figure.index,
|
||||
0x00, figure.data[0], figure.data[1], figure.data[2], figure.data[4],
|
||||
figure.data[5], figure.data[6], figure.data[7]};
|
||||
|
||||
figureChangeResponse[13] = GenerateChecksum(figureChangeResponse, 13);
|
||||
m_figure_added_removed_responses.push(figureChangeResponse);
|
||||
}
|
||||
|
||||
u8 DimensionsToypad::GenerateChecksum(const std::array<u8, 32>& data, u32 num_of_bytes) {
|
||||
int checksum = 0;
|
||||
ASSERT(num_of_bytes <= data.size());
|
||||
for (u8 i = 0; i < num_of_bytes; i++) {
|
||||
checksum += data[i];
|
||||
}
|
||||
return (checksum & 0xFF);
|
||||
}
|
||||
|
||||
void DimensionsToypad::GetBlankResponse(u8 type, u8 sequence, std::array<u8, 32>& reply_buf) {
|
||||
reply_buf[0] = 0x55;
|
||||
reply_buf[1] = type;
|
||||
reply_buf[2] = sequence;
|
||||
reply_buf[3] = GenerateChecksum(reply_buf, 3);
|
||||
}
|
||||
|
||||
void DimensionsToypad::GenerateRandomNumber(const u8* buf, u8 sequence,
|
||||
std::array<u8, 32>& reply_buf) {
|
||||
// Decrypt payload into an 8 byte array
|
||||
const std::array<u8, 8> value = Decrypt(buf, std::nullopt);
|
||||
// Seed is the first 4 bytes (little endian) of the decrypted payload
|
||||
const u32 seed = (u32&)value[0];
|
||||
// Confirmation is the second 4 bytes (big endian) of the decrypted payload
|
||||
// const u32 conf = (u32be&)value[4];
|
||||
// Initialize rng using the seed from decrypted payload
|
||||
InitializeRNG(seed);
|
||||
// Encrypt 8 bytes, first 4 bytes is the decrypted confirmation from payload, 2nd 4 bytes are
|
||||
// blank
|
||||
std::array<u8, 8> value_to_encrypt = {value[4], value[5], value[6], value[7], 0, 0, 0, 0};
|
||||
const std::array<u8, 8> encrypted = Encrypt(value_to_encrypt.data(), std::nullopt);
|
||||
reply_buf[0] = 0x55;
|
||||
reply_buf[1] = 0x09;
|
||||
reply_buf[2] = sequence;
|
||||
// Copy encrypted value to response data
|
||||
std::memcpy(&reply_buf[3], encrypted.data(), encrypted.size());
|
||||
reply_buf[11] = GenerateChecksum(reply_buf, 11);
|
||||
}
|
||||
|
||||
void DimensionsToypad::InitializeRNG(u32 seed) {
|
||||
m_random_a = 0xF1EA5EED;
|
||||
m_random_b = seed;
|
||||
m_random_c = seed;
|
||||
m_random_d = seed;
|
||||
|
||||
for (int i = 0; i < 42; i++) {
|
||||
GetNext();
|
||||
}
|
||||
}
|
||||
|
||||
u32 DimensionsToypad::GetNext() {
|
||||
const u32 e = m_random_a - std::rotl(m_random_b, 21);
|
||||
m_random_a = m_random_b ^ std::rotl(m_random_c, 19);
|
||||
m_random_b = m_random_c + std::rotl(m_random_d, 6);
|
||||
m_random_c = m_random_d + e;
|
||||
m_random_d = e + m_random_a;
|
||||
return m_random_d;
|
||||
}
|
||||
|
||||
std::array<u8, 8> DimensionsToypad::Decrypt(const u8* buf, std::optional<std::array<u8, 16>> key) {
|
||||
// Value to decrypt is separated in to two little endian 32 bit unsigned integers
|
||||
u32 data_one = u32(buf[0]) | (u32(buf[1]) << 8) | (u32(buf[2]) << 16) | (u32(buf[3]) << 24);
|
||||
u32 data_two = u32(buf[4]) | (u32(buf[5]) << 8) | (u32(buf[6]) << 16) | (u32(buf[7]) << 24);
|
||||
|
||||
// Use the key as 4 32 bit little endian unsigned integers
|
||||
u32 key_one;
|
||||
u32 key_two;
|
||||
u32 key_three;
|
||||
u32 key_four;
|
||||
|
||||
if (key) {
|
||||
key_one = u32(key.value()[0]) | (u32(key.value()[1]) << 8) | (u32(key.value()[2]) << 16) |
|
||||
(u32(key.value()[3]) << 24);
|
||||
key_two = u32(key.value()[4]) | (u32(key.value()[5]) << 8) | (u32(key.value()[6]) << 16) |
|
||||
(u32(key.value()[7]) << 24);
|
||||
key_three = u32(key.value()[8]) | (u32(key.value()[9]) << 8) |
|
||||
(u32(key.value()[10]) << 16) | (u32(key.value()[11]) << 24);
|
||||
key_four = u32(key.value()[12]) | (u32(key.value()[13]) << 8) |
|
||||
(u32(key.value()[14]) << 16) | (u32(key.value()[15]) << 24);
|
||||
} else {
|
||||
key_one = u32(COMMAND_KEY[0]) | (u32(COMMAND_KEY[1]) << 8) | (u32(COMMAND_KEY[2]) << 16) |
|
||||
(u32(COMMAND_KEY[3]) << 24);
|
||||
key_two = u32(COMMAND_KEY[4]) | (u32(COMMAND_KEY[5]) << 8) | (u32(COMMAND_KEY[6]) << 16) |
|
||||
(u32(COMMAND_KEY[7]) << 24);
|
||||
key_three = u32(COMMAND_KEY[8]) | (u32(COMMAND_KEY[9]) << 8) |
|
||||
(u32(COMMAND_KEY[10]) << 16) | (u32(COMMAND_KEY[11]) << 24);
|
||||
key_four = u32(COMMAND_KEY[12]) | (u32(COMMAND_KEY[13]) << 8) |
|
||||
(u32(COMMAND_KEY[14]) << 16) | (u32(COMMAND_KEY[15]) << 24);
|
||||
}
|
||||
|
||||
u32 sum = 0xC6EF3720;
|
||||
constexpr u32 delta = 0x9E3779B9;
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
data_two -=
|
||||
(((data_one << 4) + key_three) ^ (data_one + sum) ^ ((data_one >> 5) + key_four));
|
||||
data_one -= (((data_two << 4) + key_one) ^ (data_two + sum) ^ ((data_two >> 5) + key_two));
|
||||
sum -= delta;
|
||||
}
|
||||
|
||||
ASSERT_MSG(sum == 0, "Decryption failed, sum inequal to 0");
|
||||
|
||||
std::array<u8, 8> decrypted = {u8(data_one & 0xFF), u8((data_one >> 8) & 0xFF),
|
||||
u8((data_one >> 16) & 0xFF), u8((data_one >> 24) & 0xFF),
|
||||
u8(data_two & 0xFF), u8((data_two >> 8) & 0xFF),
|
||||
u8((data_two >> 16) & 0xFF), u8((data_two >> 24) & 0xFF)};
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
std::array<u8, 8> DimensionsToypad::Encrypt(const u8* buf, std::optional<std::array<u8, 16>> key) {
|
||||
// Value to encrypt is separated in to two little endian 32 bit unsigned integers
|
||||
u32 data_one = u32(buf[0]) | (u32(buf[1]) << 8) | (u32(buf[2]) << 16) | (u32(buf[3]) << 24);
|
||||
u32 data_two = u32(buf[4]) | (u32(buf[5]) << 8) | (u32(buf[6]) << 16) | (u32(buf[7]) << 24);
|
||||
|
||||
// Use the key as 4 32 bit little endian unsigned integers
|
||||
u32 key_one;
|
||||
u32 key_two;
|
||||
u32 key_three;
|
||||
u32 key_four;
|
||||
|
||||
if (key) {
|
||||
key_one = u32(key.value()[0]) | (u32(key.value()[1]) << 8) | (u32(key.value()[2]) << 16) |
|
||||
(u32(key.value()[3]) << 24);
|
||||
key_two = u32(key.value()[4]) | (u32(key.value()[5]) << 8) | (u32(key.value()[6]) << 16) |
|
||||
(u32(key.value()[7]) << 24);
|
||||
key_three = u32(key.value()[8]) | (u32(key.value()[9]) << 8) |
|
||||
(u32(key.value()[10]) << 16) | (u32(key.value()[11]) << 24);
|
||||
key_four = u32(key.value()[12]) | (u32(key.value()[13]) << 8) |
|
||||
(u32(key.value()[14]) << 16) | (u32(key.value()[15]) << 24);
|
||||
} else {
|
||||
key_one = u32(COMMAND_KEY[0]) | (u32(COMMAND_KEY[1]) << 8) | (u32(COMMAND_KEY[2]) << 16) |
|
||||
(u32(COMMAND_KEY[3]) << 24);
|
||||
key_two = u32(COMMAND_KEY[4]) | (u32(COMMAND_KEY[5]) << 8) | (u32(COMMAND_KEY[6]) << 16) |
|
||||
(u32(COMMAND_KEY[7]) << 24);
|
||||
key_three = u32(COMMAND_KEY[8]) | (u32(COMMAND_KEY[9]) << 8) |
|
||||
(u32(COMMAND_KEY[10]) << 16) | (u32(COMMAND_KEY[11]) << 24);
|
||||
key_four = u32(COMMAND_KEY[12]) | (u32(COMMAND_KEY[13]) << 8) |
|
||||
(u32(COMMAND_KEY[14]) << 16) | (u32(COMMAND_KEY[15]) << 24);
|
||||
}
|
||||
|
||||
u32 sum = 0;
|
||||
u32 delta = 0x9E3779B9;
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
sum += delta;
|
||||
data_one += (((data_two << 4) + key_one) ^ (data_two + sum) ^ ((data_two >> 5) + key_two));
|
||||
data_two +=
|
||||
(((data_one << 4) + key_three) ^ (data_one + sum) ^ ((data_one >> 5) + key_four));
|
||||
}
|
||||
|
||||
std::array<u8, 8> encrypted = {u8(data_one & 0xFF), u8((data_one >> 8) & 0xFF),
|
||||
u8((data_one >> 16) & 0xFF), u8((data_one >> 24) & 0xFF),
|
||||
u8(data_two & 0xFF), u8((data_two >> 8) & 0xFF),
|
||||
u8((data_two >> 16) & 0xFF), u8((data_two >> 24) & 0xFF)};
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
std::array<u8, 16> DimensionsToypad::GenerateFigureKey(const std::array<u8, 0x2D * 0x04>& buf) {
|
||||
std::array<u8, 7> uid = {buf[0], buf[1], buf[2], buf[4], buf[5], buf[6], buf[7]};
|
||||
|
||||
u32 scrambleA = Scramble(uid, 3);
|
||||
u32 scrambleB = Scramble(uid, 4);
|
||||
u32 scrambleC = Scramble(uid, 5);
|
||||
u32 scrambleD = Scramble(uid, 6);
|
||||
|
||||
return {
|
||||
u8((scrambleA >> 24) & 0xFF), u8((scrambleA >> 16) & 0xFF), u8((scrambleA >> 8) & 0xFF),
|
||||
u8(scrambleA & 0xFF), u8((scrambleB >> 24) & 0xFF), u8((scrambleB >> 16) & 0xFF),
|
||||
u8((scrambleB >> 8) & 0xFF), u8(scrambleB & 0xFF), u8((scrambleC >> 24) & 0xFF),
|
||||
u8((scrambleC >> 16) & 0xFF), u8((scrambleC >> 8) & 0xFF), u8(scrambleC & 0xFF),
|
||||
u8((scrambleD >> 24) & 0xFF), u8((scrambleD >> 16) & 0xFF), u8((scrambleD >> 8) & 0xFF),
|
||||
u8(scrambleD & 0xFF)};
|
||||
}
|
||||
|
||||
u32 DimensionsToypad::Scramble(const std::array<u8, 7>& uid, u8 count) {
|
||||
std::vector<u8> to_scramble;
|
||||
to_scramble.reserve(uid.size() + CHAR_CONSTANT.size());
|
||||
for (u8 x : uid) {
|
||||
to_scramble.push_back(x);
|
||||
}
|
||||
for (u8 c : CHAR_CONSTANT) {
|
||||
to_scramble.push_back(c);
|
||||
}
|
||||
to_scramble[(count * 4) - 1] = 0xaa;
|
||||
|
||||
std::array<u8, 4> randomized = DimensionsRandomize(to_scramble, count);
|
||||
|
||||
return (u32(randomized[0]) << 24) | (u32(randomized[1]) << 16) | (u32(randomized[2]) << 8) |
|
||||
u32(randomized[3]);
|
||||
}
|
||||
|
||||
std::array<u8, 4> DimensionsToypad::PWDGenerate(const std::array<u8, 7>& uid) {
|
||||
std::vector<u8> pwdCalc = {PWD_CONSTANT.begin(), PWD_CONSTANT.end() - 1};
|
||||
for (u8 i = 0; i < uid.size(); i++) {
|
||||
pwdCalc.insert(pwdCalc.begin() + i, uid[i]);
|
||||
}
|
||||
|
||||
return DimensionsRandomize(pwdCalc, 8);
|
||||
}
|
||||
|
||||
std::array<u8, 4> DimensionsToypad::DimensionsRandomize(const std::vector<u8>& key, u8 count) {
|
||||
u32 scrambled = 0;
|
||||
for (u8 i = 0; i < count; i++) {
|
||||
const u32 v4 = std::rotr(scrambled, 25);
|
||||
const u32 v5 = std::rotr(scrambled, 10);
|
||||
const u32 b = u32(key[i * 4]) | (u32(key[(i * 4) + 1]) << 8) |
|
||||
(u32(key[(i * 4) + 2]) << 16) | (u32(key[(i * 4) + 3]) << 24);
|
||||
scrambled = b + v4 + v5 - scrambled;
|
||||
}
|
||||
return {u8(scrambled & 0xFF), u8(scrambled >> 8 & 0xFF), u8(scrambled >> 16 & 0xFF),
|
||||
u8(scrambled >> 24 & 0xFF)};
|
||||
}
|
||||
|
||||
u32 DimensionsToypad::GetFigureId(const std::array<u8, 0x2D * 0x04>& buf) {
|
||||
const std::array<u8, 16> figure_key = GenerateFigureKey(buf);
|
||||
|
||||
const std::array<u8, 8> decrypted = Decrypt(&buf[36 * 4], figure_key);
|
||||
|
||||
const u32 fig_num = u32(decrypted[0]) | (u32(decrypted[1]) << 8) | (u32(decrypted[2]) << 16) |
|
||||
(u32(decrypted[3]) << 24);
|
||||
// Characters have their model number encrypted in page 36
|
||||
if (fig_num < 1000) {
|
||||
return fig_num;
|
||||
}
|
||||
// Vehicles/Gadgets have their model number written as little endian in page 36
|
||||
return u32(buf[36 * 4]) | (u32(buf[(36 * 4) + 1]) << 8) | (u32(buf[(36 * 4) + 2]) << 16) |
|
||||
(u32(buf[(36 * 4) + 3]) << 24);
|
||||
}
|
||||
|
||||
DimensionsFigure& DimensionsToypad::GetFigureByIndex(u8 index) {
|
||||
return m_figures[index];
|
||||
}
|
||||
|
||||
void DimensionsToypad::RandomUID(u8* uid_buffer) {
|
||||
uid_buffer[0] = 0x04;
|
||||
uid_buffer[7] = 0x80;
|
||||
|
||||
for (u8 i = 1; i < 7; i++) {
|
||||
u8 random = rand() % 255;
|
||||
uid_buffer[i] = random;
|
||||
}
|
||||
}
|
||||
|
||||
void DimensionsToypad::GetChallengeResponse(const u8* buf, u8 sequence,
|
||||
std::array<u8, 32>& reply_buf) {
|
||||
// Decrypt payload into an 8 byte array
|
||||
const std::array<u8, 8> value = Decrypt(buf, std::nullopt);
|
||||
// Confirmation is the first 4 bytes of the decrypted payload
|
||||
// const u32 conf = read_from_ptr<be_t<u32>>(value);
|
||||
// Generate next random number based on RNG
|
||||
const u32 next_random = GetNext();
|
||||
// Encrypt an 8 byte array, first 4 bytes are the next random number (little endian)
|
||||
// followed by the confirmation from the decrypted payload
|
||||
std::array<u8, 8> value_to_encrypt = {u8(next_random & 0xFF),
|
||||
u8((next_random >> 8) & 0xFF),
|
||||
u8((next_random >> 16) & 0xFF),
|
||||
u8((next_random >> 24) & 0xFF),
|
||||
value[0],
|
||||
value[1],
|
||||
value[2],
|
||||
value[3]};
|
||||
const std::array<u8, 8> encrypted = Encrypt(value_to_encrypt.data(), std::nullopt);
|
||||
reply_buf[0] = 0x55;
|
||||
reply_buf[1] = 0x09;
|
||||
reply_buf[2] = sequence;
|
||||
// Copy encrypted value to response data
|
||||
std::memcpy(&reply_buf[3], encrypted.data(), encrypted.size());
|
||||
reply_buf[11] = GenerateChecksum(reply_buf, 11);
|
||||
}
|
||||
|
||||
void DimensionsToypad::QueryBlock(u8 index, u8 page, std::array<u8, 32>& reply_buf, u8 sequence) {
|
||||
std::lock_guard lock(m_dimensions_mutex);
|
||||
|
||||
reply_buf[0] = 0x55;
|
||||
reply_buf[1] = 0x12;
|
||||
reply_buf[2] = sequence;
|
||||
reply_buf[3] = 0x00;
|
||||
|
||||
// Index from game begins at 1 rather than 0, so minus 1 here
|
||||
if (const u8 figure_index = index - 1; figure_index < MAX_DIMENSIONS_FIGURES) {
|
||||
const DimensionsFigure& figure = GetFigureByIndex(figure_index);
|
||||
|
||||
// Query 4 pages of 4 bytes from the figure, copy this to the response
|
||||
if (figure.index != 255 && (4 * page) < ((0x2D * 4) - 16)) {
|
||||
std::memcpy(&reply_buf[4], figure.data.data() + (4 * page), 16);
|
||||
}
|
||||
}
|
||||
reply_buf[20] = GenerateChecksum(reply_buf, 20);
|
||||
}
|
||||
|
||||
void DimensionsToypad::WriteBlock(u8 index, u8 page, const u8* to_write_buf,
|
||||
std::array<u8, 32>& reply_buf, u8 sequence) {
|
||||
std::lock_guard lock(m_dimensions_mutex);
|
||||
|
||||
reply_buf[0] = 0x55;
|
||||
reply_buf[1] = 0x02;
|
||||
reply_buf[2] = sequence;
|
||||
reply_buf[3] = 0x00;
|
||||
|
||||
// Index from game begins at 1 rather than 0, so minus 1 here
|
||||
if (const u8 figure_index = index - 1; figure_index < MAX_DIMENSIONS_FIGURES) {
|
||||
DimensionsFigure& figure = GetFigureByIndex(figure_index);
|
||||
|
||||
// Copy 4 bytes to the page on the figure requested by the game
|
||||
if (figure.index != 255 && page < 0x2D) {
|
||||
// Id is written to page 36
|
||||
if (page == 36) {
|
||||
figure.id = u32(to_write_buf[0]) | (u32(to_write_buf[1]) << 8) |
|
||||
(u32(to_write_buf[2]) << 16) | (u32(to_write_buf[3]) << 24);
|
||||
}
|
||||
std::memcpy(figure.data.data() + (page * 4), to_write_buf, 4);
|
||||
figure.Save();
|
||||
}
|
||||
}
|
||||
reply_buf[4] = GenerateChecksum(reply_buf, 4);
|
||||
}
|
||||
|
||||
void DimensionsToypad::GetModel(const u8* buf, u8 sequence, std::array<u8, 32>& reply_buf) {
|
||||
// Decrypt payload to 8 byte array, byte 1 is the index, 4-7 are the confirmation
|
||||
const std::array<u8, 8> value = Decrypt(buf, std::nullopt);
|
||||
const u8 index = value[0];
|
||||
// const u32 conf = read_from_ptr<be_t<u32>>(value, 4);
|
||||
std::array<u8, 8> value_to_encrypt = {};
|
||||
// Response is the figure's id (little endian) followed by the confirmation from payload
|
||||
// Index from game begins at 1 rather than 0, so minus 1 here
|
||||
if (const u8 figure_index = index - 1; figure_index < MAX_DIMENSIONS_FIGURES) {
|
||||
const DimensionsFigure& figure = GetFigureByIndex(figure_index);
|
||||
value_to_encrypt = {u8(figure.id & 0xFF),
|
||||
u8((figure.id >> 8) & 0xFF),
|
||||
u8((figure.id >> 16) & 0xFF),
|
||||
u8((figure.id >> 24) & 0xFF),
|
||||
value[4],
|
||||
value[5],
|
||||
value[6],
|
||||
value[7]};
|
||||
}
|
||||
const std::array<u8, 8> encrypted = Encrypt(value_to_encrypt.data(), std::nullopt);
|
||||
reply_buf[0] = 0x55;
|
||||
reply_buf[1] = 0x0a;
|
||||
reply_buf[2] = sequence;
|
||||
reply_buf[3] = 0x00;
|
||||
// Copy encrypted message to response
|
||||
std::memcpy(&reply_buf[4], encrypted.data(), encrypted.size());
|
||||
reply_buf[12] = GenerateChecksum(reply_buf, 12);
|
||||
}
|
||||
|
||||
std::optional<std::array<u8, 32>> DimensionsToypad::PopAddedRemovedResponse() {
|
||||
std::lock_guard lock(m_dimensions_mutex);
|
||||
|
||||
if (m_figure_added_removed_responses.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::array<u8, 32> response = m_figure_added_removed_responses.front();
|
||||
m_figure_added_removed_responses.pop();
|
||||
return response;
|
||||
}
|
||||
|
||||
libusb_endpoint_descriptor* DimensionsBackend::FillEndpointDescriptorPair() {
|
||||
return m_endpoint_descriptors.data();
|
||||
}
|
||||
|
||||
libusb_interface_descriptor* DimensionsBackend::FillInterfaceDescriptor(
|
||||
libusb_endpoint_descriptor* descs) {
|
||||
m_interface_descriptors[0].endpoint = descs;
|
||||
return m_interface_descriptors.data();
|
||||
}
|
||||
|
||||
libusb_config_descriptor* DimensionsBackend::FillConfigDescriptor(libusb_interface* inter) {
|
||||
m_config_descriptors[0].interface = inter;
|
||||
return m_config_descriptors.data();
|
||||
}
|
||||
|
||||
libusb_device_descriptor* DimensionsBackend::FillDeviceDescriptor() {
|
||||
return m_device_descriptors.data();
|
||||
}
|
||||
|
||||
libusb_transfer_status DimensionsBackend::HandleAsyncTransfer(libusb_transfer* transfer) {
|
||||
ASSERT(transfer->length == 32);
|
||||
|
||||
switch (transfer->endpoint) {
|
||||
case 0x81: {
|
||||
// Read Endpoint, wait to respond with either an added/removed figure response, or a queued
|
||||
// response from a previous write
|
||||
bool responded = false;
|
||||
while (!responded) {
|
||||
std::lock_guard lock(m_query_mutex);
|
||||
std::optional<std::array<u8, 32>> response =
|
||||
m_dimensions_toypad->PopAddedRemovedResponse();
|
||||
if (response) {
|
||||
std::memcpy(transfer->buffer, response.value().data(), 0x20);
|
||||
transfer->length = 32;
|
||||
responded = true;
|
||||
} else if (!m_queries.empty()) {
|
||||
std::memcpy(transfer->buffer, m_queries.front().data(), 0x20);
|
||||
transfer->length = 32;
|
||||
m_queries.pop();
|
||||
responded = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x01: {
|
||||
// Write endpoint, similar structure of request to the Infinity Base with a command for byte
|
||||
// 3, sequence for byte 4, the payload after that, then a checksum for the final byte.
|
||||
|
||||
const u8 command = transfer->buffer[2];
|
||||
const u8 sequence = transfer->buffer[3];
|
||||
|
||||
std::array<u8, 32> q_result{};
|
||||
|
||||
switch (command) {
|
||||
case 0xB0: // Wake
|
||||
{
|
||||
// Consistent device response to the wake command
|
||||
q_result = {0x55, 0x0e, 0x01, 0x28, 0x63, 0x29, 0x20, 0x4c, 0x45,
|
||||
0x47, 0x4f, 0x20, 0x32, 0x30, 0x31, 0x34, 0x46};
|
||||
break;
|
||||
}
|
||||
case 0xB1: // Seed
|
||||
{
|
||||
// Initialise a random number generator using the seed provided
|
||||
m_dimensions_toypad->GenerateRandomNumber(&transfer->buffer[4], sequence, q_result);
|
||||
break;
|
||||
}
|
||||
case 0xB3: // Challenge
|
||||
{
|
||||
// Get the next number in the sequence based on the RNG from 0xB1 command
|
||||
m_dimensions_toypad->GetChallengeResponse(&transfer->buffer[4], sequence, q_result);
|
||||
break;
|
||||
}
|
||||
case 0xC0: // Color
|
||||
case 0xC1: // Get Pad Color
|
||||
case 0xC2: // Fade
|
||||
case 0xC3: // Flash
|
||||
case 0xC4: // Fade Random
|
||||
case 0xC6: // Fade All
|
||||
case 0xC7: // Flash All
|
||||
case 0xC8: // Color All
|
||||
{
|
||||
// Send a blank response to acknowledge color has been sent to toypad
|
||||
m_dimensions_toypad->GetBlankResponse(0x01, sequence, q_result);
|
||||
break;
|
||||
}
|
||||
case 0xD2: // Read
|
||||
{
|
||||
// Read 4 pages from the figure at index (buf[4]), starting with page buf[5]
|
||||
m_dimensions_toypad->QueryBlock(transfer->buffer[4], transfer->buffer[5], q_result,
|
||||
sequence);
|
||||
break;
|
||||
}
|
||||
case 0xD3: // Write
|
||||
{
|
||||
// Write 4 bytes to page buf[5] to the figure at index buf[4]
|
||||
m_dimensions_toypad->WriteBlock(transfer->buffer[4], transfer->buffer[5],
|
||||
&transfer->buffer[6], q_result, sequence);
|
||||
break;
|
||||
}
|
||||
case 0xD4: // Model
|
||||
{
|
||||
// Get the model id of the figure at index buf[4]
|
||||
m_dimensions_toypad->GetModel(&transfer->buffer[4], sequence, q_result);
|
||||
break;
|
||||
}
|
||||
case 0xD0: // Tag List
|
||||
case 0xE1: // PWD
|
||||
case 0xE5: // Active
|
||||
case 0xFF: // LEDS Query
|
||||
{
|
||||
// Further investigation required
|
||||
LOG_ERROR(Lib_Usbd, "Unimplemented LD Function: {:x}", command);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LOG_ERROR(Lib_Usbd, "Unknown LD Function: {:x}", command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::lock_guard lock(m_query_mutex);
|
||||
m_queries.push(q_result);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return LIBUSB_TRANSFER_COMPLETED;
|
||||
}
|
||||
|
||||
s32 DimensionsBackend::SubmitTransfer(libusb_transfer* transfer) {
|
||||
if (transfer->endpoint == 0x01) {
|
||||
std::thread write_thread([this, transfer] {
|
||||
HandleAsyncTransfer(transfer);
|
||||
|
||||
const u8 flags = transfer->flags;
|
||||
transfer->status = LIBUSB_TRANSFER_COMPLETED;
|
||||
transfer->actual_length = transfer->length;
|
||||
if (transfer->callback) {
|
||||
transfer->callback(transfer);
|
||||
}
|
||||
if (flags & LIBUSB_TRANSFER_FREE_TRANSFER) {
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
});
|
||||
write_thread.detach();
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
return UsbEmulatedBackend::SubmitTransfer(transfer);
|
||||
}
|
||||
|
||||
void DimensionsFigure::Save() {
|
||||
if (!dimFile.IsOpen())
|
||||
return;
|
||||
|
||||
dimFile.Seek(0);
|
||||
dimFile.Write(data);
|
||||
}
|
||||
} // namespace Libraries::Usbd
|
||||
118
src/core/libraries/usbd/emulated/dimensions.h
Normal file
118
src/core/libraries/usbd/emulated/dimensions.h
Normal file
@ -0,0 +1,118 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
|
||||
#include "common/io_file.h"
|
||||
#include "core/libraries/usbd/usb_backend.h"
|
||||
|
||||
namespace Libraries::Usbd {
|
||||
|
||||
constexpr u8 DIMEMSIONS_BLOCK_COUNT = 0x2D;
|
||||
constexpr u8 DIMENSIONS_BLOCK_SIZE = 0x04;
|
||||
constexpr u8 DIMENSIONS_FIGURE_SIZE = DIMEMSIONS_BLOCK_COUNT * DIMENSIONS_BLOCK_SIZE;
|
||||
constexpr u8 MAX_DIMENSIONS_FIGURES = 7;
|
||||
|
||||
struct DimensionsFigure final {
|
||||
Common::FS::IOFile dimFile;
|
||||
std::array<u8, DIMENSIONS_FIGURE_SIZE> data{};
|
||||
u8 index = 255;
|
||||
u8 pad = 255;
|
||||
u32 id = 0;
|
||||
void Save();
|
||||
};
|
||||
|
||||
class DimensionsToypad final : public UsbEmulatedImpl {
|
||||
public:
|
||||
DimensionsToypad();
|
||||
~DimensionsToypad() override = default;
|
||||
|
||||
static void GetBlankResponse(u8 type, u8 sequence, std::array<u8, 32>& reply_buf);
|
||||
void GenerateRandomNumber(const u8* buf, u8 sequence, std::array<u8, 32>& reply_buf);
|
||||
void InitializeRNG(u32 seed);
|
||||
void GetChallengeResponse(const u8* buf, u8 sequence, std::array<u8, 32>& reply_buf);
|
||||
void QueryBlock(u8 index, u8 page, std::array<u8, 32>& reply_buf, u8 sequence);
|
||||
void WriteBlock(u8 index, u8 page, const u8* to_write_buf, std::array<u8, 32>& reply_buf,
|
||||
u8 sequence);
|
||||
void GetModel(const u8* buf, u8 sequence, std::array<u8, 32>& reply_buf);
|
||||
std::optional<std::array<u8, 32>> PopAddedRemovedResponse();
|
||||
|
||||
void LoadFigure(std::string file_name, u8 pad, u8 slot) override;
|
||||
void RemoveFigure(u8 pad, u8 slot, bool full_remove) override;
|
||||
void MoveFigure(u8 new_pad, u8 new_index, u8 old_pad, u8 old_index) override;
|
||||
void TempRemoveFigure(u8 index) override;
|
||||
void CancelRemoveFigure(u8 index) override;
|
||||
|
||||
u32 LoadDimensionsFigure(const std::array<u8, 0x2D * 0x04>& buf, Common::FS::IOFile file,
|
||||
u8 pad, u8 index);
|
||||
|
||||
protected:
|
||||
std::mutex m_dimensions_mutex;
|
||||
std::array<DimensionsFigure, MAX_DIMENSIONS_FIGURES> m_figures{};
|
||||
|
||||
private:
|
||||
static void RandomUID(u8* uid_buffer);
|
||||
static u8 GenerateChecksum(const std::array<u8, 32>& data, u32 num_of_bytes);
|
||||
static std::array<u8, 8> Decrypt(const u8* buf, std::optional<std::array<u8, 16>> key);
|
||||
static std::array<u8, 8> Encrypt(const u8* buf, std::optional<std::array<u8, 16>> key);
|
||||
static std::array<u8, 16> GenerateFigureKey(const std::array<u8, 0x2D * 0x04>& buf);
|
||||
static u32 Scramble(const std::array<u8, 7>& uid, u8 count);
|
||||
static std::array<u8, 4> PWDGenerate(const std::array<u8, 7>& uid);
|
||||
static std::array<u8, 4> DimensionsRandomize(const std::vector<u8>& key, u8 count);
|
||||
static u32 GetFigureId(const std::array<u8, 0x2D * 0x04>& buf);
|
||||
u32 GetNext();
|
||||
DimensionsFigure& GetFigureByIndex(u8 index);
|
||||
|
||||
u32 m_random_a{};
|
||||
u32 m_random_b{};
|
||||
u32 m_random_c{};
|
||||
u32 m_random_d{};
|
||||
|
||||
u8 m_figure_order = 0;
|
||||
std::queue<std::array<u8, 32>> m_figure_added_removed_responses;
|
||||
};
|
||||
|
||||
class DimensionsBackend final : public UsbEmulatedBackend {
|
||||
protected:
|
||||
libusb_endpoint_descriptor* FillEndpointDescriptorPair() override;
|
||||
libusb_interface_descriptor* FillInterfaceDescriptor(
|
||||
libusb_endpoint_descriptor* descs) override;
|
||||
libusb_config_descriptor* FillConfigDescriptor(libusb_interface* inter) override;
|
||||
libusb_device_descriptor* FillDeviceDescriptor() override;
|
||||
|
||||
s32 GetMaxPacketSize(libusb_device* dev, u8 endpoint) override {
|
||||
return 32;
|
||||
}
|
||||
|
||||
s32 SubmitTransfer(libusb_transfer* transfer) override;
|
||||
|
||||
libusb_transfer_status HandleAsyncTransfer(libusb_transfer* transfer) override;
|
||||
|
||||
std::shared_ptr<UsbEmulatedImpl> GetImplRef() override {
|
||||
return m_dimensions_toypad;
|
||||
}
|
||||
|
||||
std::mutex m_query_mutex;
|
||||
std::queue<std::array<u8, 32>> m_queries;
|
||||
|
||||
private:
|
||||
std::shared_ptr<DimensionsToypad> m_dimensions_toypad = std::make_shared<DimensionsToypad>();
|
||||
|
||||
std::array<u8, 9> m_endpoint_out_extra = {0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x1d, 0x00};
|
||||
std::vector<libusb_endpoint_descriptor> m_endpoint_descriptors = {
|
||||
{0x7, 0x5, 0x81, 0x3, 0x20, 0x1, 0x0, 0x0}, {0x7, 0x5, 0x1, 0x3, 0x20, 0x1, 0x0, 0x0}};
|
||||
std::vector<libusb_interface_descriptor> m_interface_descriptors = {
|
||||
{0x9, 0x4, 0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0}};
|
||||
std::vector<libusb_config_descriptor> m_config_descriptors = {
|
||||
{0x9, 0x2, 0x29, 0x1, 0x1, 0x0, 0x80, 0xFA}};
|
||||
std::vector<libusb_device_descriptor> m_device_descriptors = {
|
||||
{0x12, 0x1, 0x200, 0x0, 0x0, 0x0, 0x20, 0x0E6F, 0x0241, 0x200, 0x1, 0x2, 0x0, 0x1}};
|
||||
};
|
||||
} // namespace Libraries::Usbd
|
||||
392
src/core/libraries/usbd/emulated/infinity.cpp
Normal file
392
src/core/libraries/usbd/emulated/infinity.cpp
Normal file
@ -0,0 +1,392 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "infinity.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace Libraries::Usbd {
|
||||
|
||||
InfinityBase::InfinityBase() {}
|
||||
|
||||
void InfinityBase::LoadFigure(std::string file_name, u8 pad, u8 slot) {
|
||||
Common::FS::IOFile file(file_name, Common::FS::FileAccessMode::ReadWrite);
|
||||
std::array<u8, INFINITY_FIGURE_SIZE> data;
|
||||
ASSERT(file.Read(data) == data.size());
|
||||
LoadInfinityFigure(data, std::move(file), slot);
|
||||
}
|
||||
|
||||
void InfinityBase::RemoveFigure(u8 pad, u8 slot, bool full_remove) {
|
||||
std::lock_guard lock(infinity_mutex);
|
||||
InfinityFigure& figure = infinity_figures[slot];
|
||||
|
||||
if (!figure.present) {
|
||||
return;
|
||||
}
|
||||
|
||||
slot = DeriveFigurePosition(slot);
|
||||
if (slot == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
figure.present = false;
|
||||
|
||||
std::array<u8, 32> figure_change_response = {0xab, 0x04, slot, 0x09, figure.order_added, 0x01};
|
||||
figure_change_response[6] = GenerateChecksum(figure_change_response, 6);
|
||||
m_figure_added_removed_responses.push(figure_change_response);
|
||||
|
||||
figure.Save();
|
||||
figure.infFile.Close();
|
||||
}
|
||||
|
||||
void InfinityBase::LoadInfinityFigure(const std::array<u8, INFINITY_FIGURE_SIZE>& buf,
|
||||
Common::FS::IOFile file, u8 position) {
|
||||
std::lock_guard lock(infinity_mutex);
|
||||
u8 order_added;
|
||||
|
||||
InfinityFigure& figure = infinity_figures[position];
|
||||
|
||||
figure.infFile = std::move(file);
|
||||
memcpy(figure.data.data(), buf.data(), figure.data.size());
|
||||
figure.present = true;
|
||||
if (figure.order_added == 255) {
|
||||
figure.order_added = m_figure_order;
|
||||
m_figure_order++;
|
||||
}
|
||||
order_added = figure.order_added;
|
||||
|
||||
position = DeriveFigurePosition(position);
|
||||
if (position == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<u8, 32> figure_change_response = {0xab, 0x04, position, 0x09, order_added, 0x00};
|
||||
figure_change_response[6] = GenerateChecksum(figure_change_response, 6);
|
||||
m_figure_added_removed_responses.push(figure_change_response);
|
||||
}
|
||||
|
||||
void InfinityBase::GetBlankResponse(u8 sequence, std::array<u8, 32>& reply_buf) {
|
||||
reply_buf[0] = 0xaa;
|
||||
reply_buf[1] = 0x01;
|
||||
reply_buf[2] = sequence;
|
||||
reply_buf[3] = GenerateChecksum(reply_buf, 3);
|
||||
}
|
||||
|
||||
void InfinityBase::DescrambleAndSeed(u8* buf, u8 sequence, std::array<u8, 32>& reply_buf) {
|
||||
u64 value = u64(buf[4]) << 56 | u64(buf[5]) << 48 | u64(buf[6]) << 40 | u64(buf[7]) << 32 |
|
||||
u64(buf[8]) << 24 | u64(buf[9]) << 16 | u64(buf[10]) << 8 | u64(buf[11]);
|
||||
u32 seed = Descramble(value);
|
||||
GenerateSeed(seed);
|
||||
GetBlankResponse(sequence, reply_buf);
|
||||
}
|
||||
|
||||
void InfinityBase::GetNextAndScramble(u8 sequence, std::array<u8, 32>& reply_buf) {
|
||||
const u32 next_random = GetNext();
|
||||
const u64 scrambled_next_random = Scramble(next_random, 0);
|
||||
reply_buf = {0xAA, 0x09, sequence};
|
||||
reply_buf[3] = u8((scrambled_next_random >> 56) & 0xFF);
|
||||
reply_buf[4] = u8((scrambled_next_random >> 48) & 0xFF);
|
||||
reply_buf[5] = u8((scrambled_next_random >> 40) & 0xFF);
|
||||
reply_buf[6] = u8((scrambled_next_random >> 32) & 0xFF);
|
||||
reply_buf[7] = u8((scrambled_next_random >> 24) & 0xFF);
|
||||
reply_buf[8] = u8((scrambled_next_random >> 16) & 0xFF);
|
||||
reply_buf[9] = u8((scrambled_next_random >> 8) & 0xFF);
|
||||
reply_buf[10] = u8(scrambled_next_random & 0xFF);
|
||||
reply_buf[11] = GenerateChecksum(reply_buf, 11);
|
||||
}
|
||||
|
||||
void InfinityBase::GetPresentFigures(u8 sequence, std::array<u8, 32>& reply_buf) {
|
||||
int x = 3;
|
||||
for (u8 i = 0; i < infinity_figures.size(); i++) {
|
||||
u8 slot = i == 0 ? 0x10 : (i < 4) ? 0x20 : 0x30;
|
||||
if (infinity_figures[i].present) {
|
||||
reply_buf[x] = slot + infinity_figures[i].order_added;
|
||||
reply_buf[x + 1] = 0x09;
|
||||
x += 2;
|
||||
}
|
||||
}
|
||||
reply_buf[0] = 0xaa;
|
||||
reply_buf[1] = x - 2;
|
||||
reply_buf[2] = sequence;
|
||||
reply_buf[x] = GenerateChecksum(reply_buf, x);
|
||||
}
|
||||
|
||||
void InfinityBase::QueryBlock(u8 fig_num, u8 block, std::array<u8, 32>& reply_buf, u8 sequence) {
|
||||
std::lock_guard lock(infinity_mutex);
|
||||
|
||||
InfinityFigure& figure = GetFigureByOrder(fig_num);
|
||||
|
||||
reply_buf[0] = 0xaa;
|
||||
reply_buf[1] = 0x12;
|
||||
reply_buf[2] = sequence;
|
||||
reply_buf[3] = 0x00;
|
||||
const u8 file_block = (block == 0) ? 1 : (block * 4);
|
||||
if (figure.present && file_block < 20) {
|
||||
memcpy(&reply_buf[4], figure.data.data() + (16 * file_block), 16);
|
||||
}
|
||||
reply_buf[20] = GenerateChecksum(reply_buf, 20);
|
||||
}
|
||||
|
||||
void InfinityBase::WriteBlock(u8 fig_num, u8 block, const u8* to_write_buf,
|
||||
std::array<u8, 32>& reply_buf, u8 sequence) {
|
||||
std::lock_guard lock(infinity_mutex);
|
||||
|
||||
InfinityFigure& figure = GetFigureByOrder(fig_num);
|
||||
|
||||
reply_buf[0] = 0xaa;
|
||||
reply_buf[1] = 0x02;
|
||||
reply_buf[2] = sequence;
|
||||
reply_buf[3] = 0x00;
|
||||
const u8 file_block = (block == 0) ? 1 : (block * 4);
|
||||
if (figure.present && file_block < 20) {
|
||||
memcpy(figure.data.data() + (file_block * 16), to_write_buf, 16);
|
||||
figure.Save();
|
||||
}
|
||||
reply_buf[4] = GenerateChecksum(reply_buf, 4);
|
||||
}
|
||||
|
||||
void InfinityBase::GetFigureIdentifier(u8 fig_num, u8 sequence, std::array<u8, 32>& reply_buf) {
|
||||
std::lock_guard lock(infinity_mutex);
|
||||
|
||||
InfinityFigure& figure = GetFigureByOrder(fig_num);
|
||||
|
||||
reply_buf[0] = 0xaa;
|
||||
reply_buf[1] = 0x09;
|
||||
reply_buf[2] = sequence;
|
||||
reply_buf[3] = 0x00;
|
||||
|
||||
if (figure.present) {
|
||||
memcpy(&reply_buf[4], figure.data.data(), 7);
|
||||
}
|
||||
reply_buf[11] = GenerateChecksum(reply_buf, 11);
|
||||
}
|
||||
|
||||
std::optional<std::array<u8, 32>> InfinityBase::PopAddedRemovedResponse() {
|
||||
if (m_figure_added_removed_responses.empty())
|
||||
return std::nullopt;
|
||||
|
||||
std::array<u8, 32> response = m_figure_added_removed_responses.front();
|
||||
m_figure_added_removed_responses.pop();
|
||||
return response;
|
||||
}
|
||||
|
||||
u8 InfinityBase::GenerateChecksum(const std::array<u8, 32>& data, int num_of_bytes) const {
|
||||
int checksum = 0;
|
||||
for (int i = 0; i < num_of_bytes; i++) {
|
||||
checksum += data[i];
|
||||
}
|
||||
return (checksum & 0xFF);
|
||||
}
|
||||
|
||||
u32 InfinityBase::Descramble(u64 num_to_descramble) {
|
||||
u64 mask = 0x8E55AA1B3999E8AA;
|
||||
u32 ret = 0;
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
if (mask & 0x8000000000000000) {
|
||||
ret = (ret << 1) | (num_to_descramble & 0x01);
|
||||
}
|
||||
|
||||
num_to_descramble >>= 1;
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u64 InfinityBase::Scramble(u32 num_to_scramble, u32 garbage) {
|
||||
u64 mask = 0x8E55AA1B3999E8AA;
|
||||
u64 ret = 0;
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
ret <<= 1;
|
||||
|
||||
if ((mask & 1) != 0) {
|
||||
ret |= (num_to_scramble & 1);
|
||||
num_to_scramble >>= 1;
|
||||
} else {
|
||||
ret |= (garbage & 1);
|
||||
garbage >>= 1;
|
||||
}
|
||||
|
||||
mask >>= 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void InfinityBase::GenerateSeed(u32 seed) {
|
||||
random_a = 0xF1EA5EED;
|
||||
random_b = seed;
|
||||
random_c = seed;
|
||||
random_d = seed;
|
||||
|
||||
for (int i = 0; i < 23; i++) {
|
||||
GetNext();
|
||||
}
|
||||
}
|
||||
|
||||
u32 InfinityBase::GetNext() {
|
||||
u32 a = random_a;
|
||||
u32 b = random_b;
|
||||
u32 c = random_c;
|
||||
u32 ret = std::rotl(random_b, 27);
|
||||
|
||||
const u32 temp = (a + ((ret ^ 0xFFFFFFFF) + 1));
|
||||
b ^= std::rotl(c, 17);
|
||||
a = random_d;
|
||||
c += a;
|
||||
ret = b + temp;
|
||||
a += temp;
|
||||
|
||||
random_c = a;
|
||||
random_a = b;
|
||||
random_b = c;
|
||||
random_d = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
InfinityFigure& InfinityBase::GetFigureByOrder(u8 order_added) {
|
||||
for (u8 i = 0; i < infinity_figures.size(); i++) {
|
||||
if (infinity_figures[i].order_added == order_added) {
|
||||
return infinity_figures[i];
|
||||
}
|
||||
}
|
||||
return infinity_figures[0];
|
||||
}
|
||||
|
||||
u8 InfinityBase::DeriveFigurePosition(u8 position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
return 1;
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
return 2;
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
return 3;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
libusb_endpoint_descriptor* InfinityBackend::FillEndpointDescriptorPair() {
|
||||
return m_endpoint_descriptors.data();
|
||||
}
|
||||
|
||||
libusb_interface_descriptor* InfinityBackend::FillInterfaceDescriptor(
|
||||
libusb_endpoint_descriptor* descs) {
|
||||
m_interface_descriptors[0].endpoint = descs;
|
||||
return m_interface_descriptors.data();
|
||||
}
|
||||
|
||||
libusb_config_descriptor* InfinityBackend::FillConfigDescriptor(libusb_interface* inter) {
|
||||
m_config_descriptors[0].interface = inter;
|
||||
return m_config_descriptors.data();
|
||||
}
|
||||
|
||||
libusb_device_descriptor* InfinityBackend::FillDeviceDescriptor() {
|
||||
return m_device_descriptors.data();
|
||||
}
|
||||
|
||||
libusb_transfer_status InfinityBackend::HandleAsyncTransfer(libusb_transfer* transfer) {
|
||||
switch (transfer->endpoint) {
|
||||
case 0x81: {
|
||||
// Respond after FF command
|
||||
std::optional<std::array<u8, 32>> response = m_infinity_base->PopAddedRemovedResponse();
|
||||
if (response) {
|
||||
memcpy(transfer->buffer, response.value().data(), 0x20);
|
||||
} else if (!m_queries.empty()) {
|
||||
memcpy(transfer->buffer, m_queries.front().data(), 0x20);
|
||||
m_queries.pop();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x01: {
|
||||
const u8 command = transfer->buffer[2];
|
||||
const u8 sequence = transfer->buffer[3];
|
||||
LOG_INFO(Lib_Usbd, "Infinity Backend Transfer command: {:x}", command);
|
||||
|
||||
std::array<u8, 32> q_result{};
|
||||
|
||||
switch (command) {
|
||||
case 0x80: {
|
||||
q_result = {0xaa, 0x15, 0x00, 0x00, 0x0f, 0x01, 0x00, 0x03, 0x02, 0x09, 0x09, 0x43,
|
||||
0x20, 0x32, 0x62, 0x36, 0x36, 0x4b, 0x34, 0x99, 0x67, 0x31, 0x93, 0x8c};
|
||||
break;
|
||||
}
|
||||
case 0x81: {
|
||||
// Initiate Challenge
|
||||
m_infinity_base->DescrambleAndSeed(transfer->buffer, sequence, q_result);
|
||||
break;
|
||||
}
|
||||
case 0x83: {
|
||||
// Challenge Response
|
||||
m_infinity_base->GetNextAndScramble(sequence, q_result);
|
||||
break;
|
||||
}
|
||||
case 0x90:
|
||||
case 0x92:
|
||||
case 0x93:
|
||||
case 0x95:
|
||||
case 0x96: {
|
||||
// Color commands
|
||||
m_infinity_base->GetBlankResponse(sequence, q_result);
|
||||
break;
|
||||
}
|
||||
case 0xA1: {
|
||||
// Get Present Figures
|
||||
m_infinity_base->GetPresentFigures(sequence, q_result);
|
||||
break;
|
||||
}
|
||||
case 0xA2: {
|
||||
// Read Block from Figure
|
||||
m_infinity_base->QueryBlock(transfer->buffer[4], transfer->buffer[5], q_result,
|
||||
sequence);
|
||||
break;
|
||||
}
|
||||
case 0xA3: {
|
||||
// Write block to figure
|
||||
m_infinity_base->WriteBlock(transfer->buffer[4], transfer->buffer[5],
|
||||
&transfer->buffer[7], q_result, sequence);
|
||||
break;
|
||||
}
|
||||
case 0xB4: {
|
||||
// Get figure ID
|
||||
m_infinity_base->GetFigureIdentifier(transfer->buffer[4], sequence, q_result);
|
||||
break;
|
||||
}
|
||||
case 0xB5: {
|
||||
// Get status?
|
||||
m_infinity_base->GetBlankResponse(sequence, q_result);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(Lib_Usbd, "Unhandled Infinity Query: {}", command);
|
||||
break;
|
||||
}
|
||||
|
||||
m_queries.push(q_result);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(Lib_Usbd, "Unhandled Infinity Endpoint: {}", transfer->endpoint);
|
||||
break;
|
||||
}
|
||||
return LIBUSB_TRANSFER_COMPLETED;
|
||||
}
|
||||
|
||||
void InfinityFigure::Save() {
|
||||
if (!infFile.IsOpen())
|
||||
return;
|
||||
|
||||
infFile.Seek(0);
|
||||
infFile.Write(data);
|
||||
}
|
||||
} // namespace Libraries::Usbd
|
||||
112
src/core/libraries/usbd/emulated/infinity.h
Normal file
112
src/core/libraries/usbd/emulated/infinity.h
Normal file
@ -0,0 +1,112 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
|
||||
#include "common/io_file.h"
|
||||
#include "core/libraries/usbd/usb_backend.h"
|
||||
|
||||
namespace Libraries::Usbd {
|
||||
|
||||
constexpr u16 INFINITY_BLOCK_COUNT = 0x14;
|
||||
constexpr u16 INFINITY_BLOCK_SIZE = 0x10;
|
||||
constexpr u16 INFINITY_FIGURE_SIZE = INFINITY_BLOCK_COUNT * INFINITY_BLOCK_SIZE;
|
||||
constexpr u8 MAX_INFINITY_FIGURES = 9;
|
||||
|
||||
struct InfinityFigure final {
|
||||
Common::FS::IOFile infFile;
|
||||
std::array<u8, INFINITY_FIGURE_SIZE> data{};
|
||||
bool present = false;
|
||||
u8 order_added = 255;
|
||||
void Save();
|
||||
};
|
||||
|
||||
class InfinityBase final : public UsbEmulatedImpl {
|
||||
public:
|
||||
InfinityBase();
|
||||
~InfinityBase() override = default;
|
||||
|
||||
void GetBlankResponse(u8 sequence, std::array<u8, 32>& reply_buf);
|
||||
void DescrambleAndSeed(u8* buf, u8 sequence, std::array<u8, 32>& reply_buf);
|
||||
void GetNextAndScramble(u8 sequence, std::array<u8, 32>& reply_buf);
|
||||
void GetPresentFigures(u8 sequence, std::array<u8, 32>& reply_buf);
|
||||
void QueryBlock(u8 fig_num, u8 block, std::array<u8, 32>& reply_buf, u8 sequence);
|
||||
void WriteBlock(u8 fig_num, u8 block, const u8* to_write_buf, std::array<u8, 32>& reply_buf,
|
||||
u8 sequence);
|
||||
void GetFigureIdentifier(u8 fig_num, u8 sequence, std::array<u8, 32>& reply_buf);
|
||||
std::optional<std::array<u8, 32>> PopAddedRemovedResponse();
|
||||
|
||||
void LoadFigure(std::string file_name, u8 pad, u8 slot) override;
|
||||
void RemoveFigure(u8 pad, u8 slot, bool full_remove) override;
|
||||
void MoveFigure(u8 new_pad, u8 new_index, u8 old_pad, u8 old_index) override {}
|
||||
void TempRemoveFigure(u8 index) override {}
|
||||
void CancelRemoveFigure(u8 index) override {}
|
||||
|
||||
void LoadInfinityFigure(const std::array<u8, 0x14 * 0x10>& buf, Common::FS::IOFile file,
|
||||
u8 position);
|
||||
|
||||
protected:
|
||||
std::mutex infinity_mutex;
|
||||
std::array<InfinityFigure, MAX_INFINITY_FIGURES> infinity_figures;
|
||||
|
||||
private:
|
||||
u8 GenerateChecksum(const std::array<u8, 32>& data, int num_of_bytes) const;
|
||||
u32 Descramble(u64 num_to_descramble);
|
||||
u64 Scramble(u32 num_to_scramble, u32 garbage);
|
||||
void GenerateSeed(u32 seed);
|
||||
u32 GetNext();
|
||||
InfinityFigure& GetFigureByOrder(u8 order_added);
|
||||
u8 DeriveFigurePosition(u8 position);
|
||||
|
||||
u32 random_a = 0;
|
||||
u32 random_b = 0;
|
||||
u32 random_c = 0;
|
||||
u32 random_d = 0;
|
||||
|
||||
u8 m_figure_order = 0;
|
||||
std::queue<std::array<u8, 32>> m_figure_added_removed_responses;
|
||||
};
|
||||
|
||||
class InfinityBackend final : public UsbEmulatedBackend {
|
||||
protected:
|
||||
libusb_endpoint_descriptor* FillEndpointDescriptorPair() override;
|
||||
libusb_interface_descriptor* FillInterfaceDescriptor(
|
||||
libusb_endpoint_descriptor* descs) override;
|
||||
libusb_config_descriptor* FillConfigDescriptor(libusb_interface* inter) override;
|
||||
libusb_device_descriptor* FillDeviceDescriptor() override;
|
||||
|
||||
s32 ControlTransfer(libusb_device_handle* dev_handle, u8 bmRequestType, u8 bRequest, u16 wValue,
|
||||
u16 wIndex, u8* data, u16 wLength, u32 timeout) override {
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
libusb_transfer_status HandleAsyncTransfer(libusb_transfer* transfer) override;
|
||||
|
||||
std::shared_ptr<UsbEmulatedImpl> GetImplRef() override {
|
||||
return m_infinity_base;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<InfinityBase> m_infinity_base = std::make_shared<InfinityBase>();
|
||||
|
||||
std::array<u8, 9> m_endpoint_out_extra = {0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x1d, 0x00};
|
||||
std::vector<libusb_endpoint_descriptor> m_endpoint_descriptors = {
|
||||
{0x7, 0x5, 0x81, 0x3, 0x20, 0x1, 0x0, 0x0},
|
||||
{0x7, 0x5, 0x1, 0x3, 0x20, 0x1, 0x0, 0x0, m_endpoint_out_extra.data(), 9}};
|
||||
std::vector<libusb_interface_descriptor> m_interface_descriptors = {
|
||||
{0x9, 0x4, 0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0}};
|
||||
std::vector<libusb_config_descriptor> m_config_descriptors = {
|
||||
{0x9, 0x2, 0x29, 0x1, 0x1, 0x0, 0x80, 0xFA}};
|
||||
std::vector<libusb_device_descriptor> m_device_descriptors = {
|
||||
{0x12, 0x1, 0x200, 0x0, 0x0, 0x0, 0x20, 0x0E6F, 0x0129, 0x200, 0x1, 0x2, 0x3, 0x1}};
|
||||
|
||||
std::queue<std::array<u8, 32>> m_queries;
|
||||
};
|
||||
} // namespace Libraries::Usbd
|
||||
@ -312,7 +312,7 @@ public:
|
||||
const auto endpoint_descs = FillEndpointDescriptorPair();
|
||||
const auto interface_desc = FillInterfaceDescriptor(endpoint_descs);
|
||||
|
||||
const auto interface = static_cast<libusb_interface*>(calloc(1, sizeof(libusb_interface*)));
|
||||
const auto interface = static_cast<libusb_interface*>(calloc(1, sizeof(libusb_interface)));
|
||||
interface->altsetting = interface_desc;
|
||||
interface->num_altsetting = 1;
|
||||
|
||||
@ -366,7 +366,7 @@ public:
|
||||
const auto desc = FillDeviceDescriptor();
|
||||
ASSERT(desc);
|
||||
|
||||
const auto fake = static_cast<UsbDevice*>(calloc(1, sizeof(UsbDevice*)));
|
||||
const auto fake = static_cast<UsbDevice*>(calloc(1, sizeof(UsbDevice)));
|
||||
fake->bus_number = 0;
|
||||
fake->port_number = 0;
|
||||
fake->device_address = 0;
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
#include "emulated/dimensions.h"
|
||||
#include "emulated/infinity.h"
|
||||
#include "emulated/skylander.h"
|
||||
#include "usb_backend.h"
|
||||
|
||||
@ -33,10 +35,9 @@ using SceUsbdTransfer = libusb_transfer;
|
||||
using SceUsbdControlSetup = libusb_control_setup;
|
||||
using SceUsbdTransferCallback = void PS4_SYSV_ABI (*)(SceUsbdTransfer* transfer);
|
||||
|
||||
// TODO: implement emulated devices
|
||||
using SkylandersPortalBackend = SkylanderBackend;
|
||||
using InfinityBaseBackend = UsbRealBackend;
|
||||
using DimensionsToypadBackend = UsbRealBackend;
|
||||
using InfinityBaseBackend = InfinityBackend;
|
||||
using DimensionsToypadBackend = DimensionsBackend;
|
||||
|
||||
enum class SceUsbdSpeed : u32 {
|
||||
UNKNOWN = 0,
|
||||
|
||||
@ -502,19 +502,19 @@ bool Elf::IsSharedLib() {
|
||||
}
|
||||
|
||||
void Elf::ElfHeaderDebugDump(const std::filesystem::path& file_name) {
|
||||
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write,
|
||||
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create,
|
||||
Common::FS::FileType::TextFile};
|
||||
f.WriteString(ElfHeaderStr());
|
||||
}
|
||||
|
||||
void Elf::SelfHeaderDebugDump(const std::filesystem::path& file_name) {
|
||||
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write,
|
||||
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create,
|
||||
Common::FS::FileType::TextFile};
|
||||
f.WriteString(SElfHeaderStr());
|
||||
}
|
||||
|
||||
void Elf::SelfSegHeaderDebugDump(const std::filesystem::path& file_name) {
|
||||
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write,
|
||||
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create,
|
||||
Common::FS::FileType::TextFile};
|
||||
for (u16 i = 0; i < m_self.segment_count; i++) {
|
||||
f.WriteString(SELFSegHeader(i));
|
||||
@ -522,7 +522,7 @@ void Elf::SelfSegHeaderDebugDump(const std::filesystem::path& file_name) {
|
||||
}
|
||||
|
||||
void Elf::PHeaderDebugDump(const std::filesystem::path& file_name) {
|
||||
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write,
|
||||
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create,
|
||||
Common::FS::FileType::TextFile};
|
||||
if (m_elf_header.e_phentsize > 0) {
|
||||
for (u16 i = 0; i < m_elf_header.e_phnum; i++) {
|
||||
|
||||
@ -32,7 +32,7 @@ const SymbolRecord* SymbolsResolver::FindSymbol(const SymbolResolver& s) const {
|
||||
}
|
||||
|
||||
void SymbolsResolver::DebugDump(const std::filesystem::path& file_name) {
|
||||
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write,
|
||||
Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create,
|
||||
Common::FS::FileType::TextFile};
|
||||
for (const auto& symbol : m_symbols) {
|
||||
const auto ids = Common::SplitString(symbol.name, '#');
|
||||
|
||||
@ -31,6 +31,8 @@
|
||||
#include "core/file_format/trp.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/disc_map/disc_map.h"
|
||||
#include "core/libraries/font/font.h"
|
||||
#include "core/libraries/font/fontft.h"
|
||||
#include "core/libraries/libc_internal/libc_internal.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/ngs2/ngs2.h"
|
||||
@ -111,6 +113,7 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
std::string id;
|
||||
std::string title;
|
||||
std::string app_version;
|
||||
u32 sdk_version;
|
||||
u32 fw_version;
|
||||
Common::PSFAttributes psf_attributes{};
|
||||
if (param_sfo_exists) {
|
||||
@ -130,8 +133,48 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
if (const auto raw_attributes = param_sfo->GetInteger("ATTRIBUTE")) {
|
||||
psf_attributes.raw = *raw_attributes;
|
||||
}
|
||||
|
||||
// Extract sdk version from pubtool info.
|
||||
std::string_view pubtool_info =
|
||||
param_sfo->GetString("PUBTOOLINFO").value_or("Unknown value");
|
||||
u64 sdk_ver_offset = pubtool_info.find("sdk_ver");
|
||||
|
||||
if (sdk_ver_offset == pubtool_info.npos) {
|
||||
// Default to using firmware version if SDK version is not found.
|
||||
sdk_version = fw_version;
|
||||
} else {
|
||||
// Increment offset to account for sdk_ver= part of string.
|
||||
sdk_ver_offset += 8;
|
||||
u64 sdk_ver_len = pubtool_info.find(",", sdk_ver_offset);
|
||||
if (sdk_ver_len == pubtool_info.npos) {
|
||||
// If there's no more commas, this is likely the last entry of pubtool info.
|
||||
// Use string length instead.
|
||||
sdk_ver_len = pubtool_info.size();
|
||||
}
|
||||
sdk_ver_len -= sdk_ver_offset;
|
||||
std::string sdk_ver_string = pubtool_info.substr(sdk_ver_offset, sdk_ver_len).data();
|
||||
// Number is stored in base 16.
|
||||
sdk_version = std::stoi(sdk_ver_string, nullptr, 16);
|
||||
}
|
||||
}
|
||||
|
||||
auto& game_info = Common::ElfInfo::Instance();
|
||||
game_info.initialized = true;
|
||||
game_info.game_serial = id;
|
||||
game_info.title = title;
|
||||
game_info.app_ver = app_version;
|
||||
game_info.firmware_ver = fw_version & 0xFFF00000;
|
||||
game_info.raw_firmware_ver = fw_version;
|
||||
game_info.sdk_ver = sdk_version;
|
||||
game_info.psf_attributes = psf_attributes;
|
||||
|
||||
const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png");
|
||||
if (std::filesystem::exists(pic1_path)) {
|
||||
game_info.splash_path = pic1_path;
|
||||
}
|
||||
|
||||
game_info.game_folder = game_folder;
|
||||
|
||||
Config::load(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (id + ".toml"),
|
||||
true);
|
||||
|
||||
@ -194,6 +237,7 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
if (param_sfo_exists) {
|
||||
LOG_INFO(Loader, "Game id: {} Title: {}", id, title);
|
||||
LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version);
|
||||
LOG_INFO(Loader, "Compiled SDK version: {:#x}", sdk_version);
|
||||
LOG_INFO(Loader, "PSVR Supported: {}", (bool)psf_attributes.support_ps_vr.Value());
|
||||
LOG_INFO(Loader, "PSVR Required: {}", (bool)psf_attributes.require_ps_vr.Value());
|
||||
}
|
||||
@ -233,22 +277,6 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
}
|
||||
}
|
||||
|
||||
auto& game_info = Common::ElfInfo::Instance();
|
||||
game_info.initialized = true;
|
||||
game_info.game_serial = id;
|
||||
game_info.title = title;
|
||||
game_info.app_ver = app_version;
|
||||
game_info.firmware_ver = fw_version & 0xFFF00000;
|
||||
game_info.raw_firmware_ver = fw_version;
|
||||
game_info.psf_attributes = psf_attributes;
|
||||
|
||||
const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png");
|
||||
if (std::filesystem::exists(pic1_path)) {
|
||||
game_info.splash_path = pic1_path;
|
||||
}
|
||||
|
||||
game_info.game_folder = game_folder;
|
||||
|
||||
std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version);
|
||||
std::string window_title = "";
|
||||
std::string remote_url(Common::g_scm_remote_url);
|
||||
@ -473,8 +501,8 @@ void Emulator::LoadSystemModules(const std::string& game_serial) {
|
||||
{"libSceJson2.sprx", nullptr},
|
||||
{"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterLib},
|
||||
{"libSceCesCs.sprx", nullptr},
|
||||
{"libSceFont.sprx", nullptr},
|
||||
{"libSceFontFt.sprx", nullptr},
|
||||
{"libSceFont.sprx", &Libraries::Font::RegisterlibSceFont},
|
||||
{"libSceFontFt.sprx", &Libraries::FontFt::RegisterlibSceFontFt},
|
||||
{"libSceFreeTypeOt.sprx", nullptr}});
|
||||
|
||||
std::vector<std::filesystem::path> found_modules;
|
||||
|
||||
@ -250,7 +250,7 @@ void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view,
|
||||
}
|
||||
|
||||
bool MustKeepDrawing() {
|
||||
return layers.size() > 1 || DebugState.IsShowingDebugMenuBar();
|
||||
return layers.size() > 1 || change_layers.size() > 1 || DebugState.IsShowingDebugMenuBar();
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@ -559,7 +559,7 @@ void Translator::EmitFetch(const GcnInst& inst) {
|
||||
std::filesystem::create_directories(dump_dir);
|
||||
}
|
||||
const auto filename = fmt::format("vs_{:#018x}.fetch.bin", info.pgm_hash);
|
||||
const auto file = IOFile{dump_dir / filename, FileAccessMode::Write};
|
||||
const auto file = IOFile{dump_dir / filename, FileAccessMode::Create};
|
||||
file.WriteRaw<u8>(fetch_data->code, fetch_data->size);
|
||||
}
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ static void DumpSrtProgram(const Shader::Info& info, const u8* code, size_t code
|
||||
std::filesystem::create_directories(dump_dir);
|
||||
}
|
||||
const auto filename = fmt::format("{}_{:#018x}.srtprogram.txt", info.stage, info.pgm_hash);
|
||||
const auto file = IOFile{dump_dir / filename, FileAccessMode::Write, FileType::TextFile};
|
||||
const auto file = IOFile{dump_dir / filename, FileAccessMode::Create, FileType::TextFile};
|
||||
|
||||
u64 address = reinterpret_cast<u64>(code);
|
||||
u64 code_end = address + codesize;
|
||||
|
||||
@ -190,7 +190,7 @@ void CollectShaderInfoPass(IR::Program& program, const Profile& profile) {
|
||||
});
|
||||
info.buffers.push_back({
|
||||
.used_types = IR::Type::U32,
|
||||
.inline_cbuf = AmdGpu::Buffer::Placeholder(VideoCore::BufferCache::FAULT_BUFFER_SIZE),
|
||||
.inline_cbuf = AmdGpu::Buffer::Placeholder(std::numeric_limits<u32>::max()),
|
||||
.buffer_type = BufferType::FaultBuffer,
|
||||
.is_written = true,
|
||||
});
|
||||
|
||||
@ -28,7 +28,7 @@ void DumpProgram(const Program& program, const Info& info, const std::string& ty
|
||||
}
|
||||
const auto ir_filename =
|
||||
fmt::format("{}_{:#018x}.{}irprogram.txt", info.stage, info.pgm_hash, type);
|
||||
const auto ir_file = IOFile{dump_dir / ir_filename, FileAccessMode::Write, FileType::TextFile};
|
||||
const auto ir_file = IOFile{dump_dir / ir_filename, FileAccessMode::Create, FileType::TextFile};
|
||||
|
||||
size_t index{0};
|
||||
std::map<const IR::Inst*, size_t> inst_to_index;
|
||||
@ -46,7 +46,7 @@ void DumpProgram(const Program& program, const Info& info, const std::string& ty
|
||||
|
||||
const auto asl_filename = fmt::format("{}_{:#018x}.{}asl.txt", info.stage, info.pgm_hash, type);
|
||||
const auto asl_file =
|
||||
IOFile{dump_dir / asl_filename, FileAccessMode::Write, FileType::TextFile};
|
||||
IOFile{dump_dir / asl_filename, FileAccessMode::Create, FileType::TextFile};
|
||||
|
||||
for (const auto& node : program.syntax_list) {
|
||||
std::string s = IR::DumpASLNode(node, block_to_index, inst_to_index) + '\n';
|
||||
|
||||
@ -2,49 +2,42 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include "common/alignment.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/types.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/amdgpu/liverpool.h"
|
||||
#include "video_core/buffer_cache/buffer_cache.h"
|
||||
#include "video_core/buffer_cache/memory_tracker.h"
|
||||
#include "video_core/host_shaders/fault_buffer_process_comp.h"
|
||||
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||
#include "video_core/texture_cache/texture_cache.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
static constexpr size_t DataShareBufferSize = 64_KB;
|
||||
static constexpr size_t StagingBufferSize = 512_MB;
|
||||
static constexpr size_t DownloadBufferSize = 32_MB;
|
||||
static constexpr size_t UboStreamBufferSize = 64_MB;
|
||||
static constexpr size_t DownloadBufferSize = 128_MB;
|
||||
static constexpr size_t DeviceBufferSize = 128_MB;
|
||||
static constexpr size_t MaxPageFaults = 1024;
|
||||
|
||||
BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_,
|
||||
AmdGpu::Liverpool* liverpool_, TextureCache& texture_cache_,
|
||||
PageManager& tracker)
|
||||
: instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_},
|
||||
memory{Core::Memory::Instance()}, texture_cache{texture_cache_},
|
||||
fault_manager{instance, scheduler, *this, CACHING_PAGEBITS, CACHING_NUMPAGES},
|
||||
staging_buffer{instance, scheduler, MemoryUsage::Upload, StagingBufferSize},
|
||||
stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize},
|
||||
download_buffer{instance, scheduler, MemoryUsage::Download, DownloadBufferSize},
|
||||
device_buffer{instance, scheduler, MemoryUsage::DeviceLocal, DeviceBufferSize},
|
||||
gds_buffer{instance, scheduler, MemoryUsage::Stream, 0, AllFlags, DataShareBufferSize},
|
||||
bda_pagetable_buffer{instance, scheduler, MemoryUsage::DeviceLocal,
|
||||
0, AllFlags, BDA_PAGETABLE_SIZE},
|
||||
fault_buffer(instance, scheduler, MemoryUsage::DeviceLocal, 0, AllFlags, FAULT_BUFFER_SIZE) {
|
||||
0, AllFlags, BDA_PAGETABLE_SIZE} {
|
||||
Vulkan::SetObjectName(instance.GetDevice(), gds_buffer.Handle(), "GDS Buffer");
|
||||
Vulkan::SetObjectName(instance.GetDevice(), bda_pagetable_buffer.Handle(),
|
||||
"BDA Page Table Buffer");
|
||||
Vulkan::SetObjectName(instance.GetDevice(), fault_buffer.Handle(), "Fault Buffer");
|
||||
|
||||
memory_tracker = std::make_unique<MemoryTracker>(tracker);
|
||||
|
||||
@ -57,80 +50,6 @@ BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& s
|
||||
const vk::Buffer& null_buffer = slot_buffers[null_id].buffer;
|
||||
Vulkan::SetObjectName(instance.GetDevice(), null_buffer, "Null Buffer");
|
||||
|
||||
// Prepare the fault buffer parsing pipeline
|
||||
boost::container::static_vector<vk::DescriptorSetLayoutBinding, 2> bindings{
|
||||
{
|
||||
.binding = 0,
|
||||
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eCompute,
|
||||
},
|
||||
{
|
||||
.binding = 1,
|
||||
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eCompute,
|
||||
},
|
||||
};
|
||||
|
||||
const vk::DescriptorSetLayoutCreateInfo desc_layout_ci = {
|
||||
.flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR,
|
||||
.bindingCount = static_cast<u32>(bindings.size()),
|
||||
.pBindings = bindings.data(),
|
||||
};
|
||||
auto [desc_layout_result, desc_layout] =
|
||||
instance.GetDevice().createDescriptorSetLayoutUnique(desc_layout_ci);
|
||||
ASSERT_MSG(desc_layout_result == vk::Result::eSuccess,
|
||||
"Failed to create descriptor set layout: {}", vk::to_string(desc_layout_result));
|
||||
fault_process_desc_layout = std::move(desc_layout);
|
||||
|
||||
const auto& module = Vulkan::Compile(HostShaders::FAULT_BUFFER_PROCESS_COMP,
|
||||
vk::ShaderStageFlagBits::eCompute, instance.GetDevice());
|
||||
Vulkan::SetObjectName(instance.GetDevice(), module, "Fault Buffer Parser");
|
||||
|
||||
const vk::SpecializationMapEntry specialization_map_entry = {
|
||||
.constantID = 0,
|
||||
.offset = 0,
|
||||
.size = sizeof(u32),
|
||||
};
|
||||
|
||||
const vk::SpecializationInfo specialization_info = {
|
||||
.mapEntryCount = 1,
|
||||
.pMapEntries = &specialization_map_entry,
|
||||
.dataSize = sizeof(u32),
|
||||
.pData = &CACHING_PAGEBITS,
|
||||
};
|
||||
|
||||
const vk::PipelineShaderStageCreateInfo shader_ci = {
|
||||
.stage = vk::ShaderStageFlagBits::eCompute,
|
||||
.module = module,
|
||||
.pName = "main",
|
||||
.pSpecializationInfo = &specialization_info,
|
||||
};
|
||||
|
||||
const vk::PipelineLayoutCreateInfo layout_info = {
|
||||
.setLayoutCount = 1U,
|
||||
.pSetLayouts = &(*fault_process_desc_layout),
|
||||
};
|
||||
auto [layout_result, layout] = instance.GetDevice().createPipelineLayoutUnique(layout_info);
|
||||
ASSERT_MSG(layout_result == vk::Result::eSuccess, "Failed to create pipeline layout: {}",
|
||||
vk::to_string(layout_result));
|
||||
fault_process_pipeline_layout = std::move(layout);
|
||||
|
||||
const vk::ComputePipelineCreateInfo pipeline_info = {
|
||||
.stage = shader_ci,
|
||||
.layout = *fault_process_pipeline_layout,
|
||||
};
|
||||
auto [pipeline_result, pipeline] =
|
||||
instance.GetDevice().createComputePipelineUnique({}, pipeline_info);
|
||||
ASSERT_MSG(pipeline_result == vk::Result::eSuccess, "Failed to create compute pipeline: {}",
|
||||
vk::to_string(pipeline_result));
|
||||
fault_process_pipeline = std::move(pipeline);
|
||||
Vulkan::SetObjectName(instance.GetDevice(), *fault_process_pipeline,
|
||||
"Fault Buffer Parser Pipeline");
|
||||
|
||||
instance.GetDevice().destroyShaderModule(module);
|
||||
|
||||
// Set up garbage collection parameters
|
||||
if (!instance.CanReportMemoryUsage()) {
|
||||
trigger_gc_memory = DEFAULT_TRIGGER_GC_MEMORY;
|
||||
@ -656,14 +575,10 @@ BufferId BufferCache::CreateBuffer(VAddr device_addr, u32 wanted_size) {
|
||||
wanted_size = static_cast<u32>(device_addr_end - device_addr);
|
||||
const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size);
|
||||
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
|
||||
const BufferId new_buffer_id = [&] {
|
||||
std::scoped_lock lk{slot_buffers_mutex};
|
||||
return slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin,
|
||||
AllFlags | vk::BufferUsageFlagBits::eShaderDeviceAddress, size);
|
||||
}();
|
||||
const BufferId new_buffer_id =
|
||||
slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin,
|
||||
AllFlags | vk::BufferUsageFlagBits::eShaderDeviceAddress, size);
|
||||
auto& new_buffer = slot_buffers[new_buffer_id];
|
||||
const size_t size_bytes = new_buffer.SizeBytes();
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
for (const BufferId overlap_id : overlap.ids) {
|
||||
JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap);
|
||||
}
|
||||
@ -672,126 +587,7 @@ BufferId BufferCache::CreateBuffer(VAddr device_addr, u32 wanted_size) {
|
||||
}
|
||||
|
||||
void BufferCache::ProcessFaultBuffer() {
|
||||
// Run fault processing shader
|
||||
const auto [mapped, offset] = download_buffer.Map(MaxPageFaults * sizeof(u64));
|
||||
vk::BufferMemoryBarrier2 fault_buffer_barrier{
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eShaderWrite,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eShaderRead,
|
||||
.buffer = fault_buffer.Handle(),
|
||||
.offset = 0,
|
||||
.size = FAULT_BUFFER_SIZE,
|
||||
};
|
||||
vk::BufferMemoryBarrier2 download_barrier{
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eShaderWrite,
|
||||
.buffer = download_buffer.Handle(),
|
||||
.offset = offset,
|
||||
.size = MaxPageFaults * sizeof(u64),
|
||||
};
|
||||
std::array<vk::BufferMemoryBarrier2, 2> barriers{fault_buffer_barrier, download_barrier};
|
||||
vk::DescriptorBufferInfo fault_buffer_info{
|
||||
.buffer = fault_buffer.Handle(),
|
||||
.offset = 0,
|
||||
.range = FAULT_BUFFER_SIZE,
|
||||
};
|
||||
vk::DescriptorBufferInfo download_info{
|
||||
.buffer = download_buffer.Handle(),
|
||||
.offset = offset,
|
||||
.range = MaxPageFaults * sizeof(u64),
|
||||
};
|
||||
boost::container::small_vector<vk::WriteDescriptorSet, 2> writes{
|
||||
{
|
||||
.dstSet = VK_NULL_HANDLE,
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
||||
.pBufferInfo = &fault_buffer_info,
|
||||
},
|
||||
{
|
||||
.dstSet = VK_NULL_HANDLE,
|
||||
.dstBinding = 1,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
||||
.pBufferInfo = &download_info,
|
||||
},
|
||||
};
|
||||
download_buffer.Commit();
|
||||
scheduler.EndRendering();
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
cmdbuf.fillBuffer(download_buffer.Handle(), offset, MaxPageFaults * sizeof(u64), 0);
|
||||
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
|
||||
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
|
||||
.bufferMemoryBarrierCount = 2,
|
||||
.pBufferMemoryBarriers = barriers.data(),
|
||||
});
|
||||
cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, *fault_process_pipeline);
|
||||
cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, *fault_process_pipeline_layout, 0,
|
||||
writes);
|
||||
constexpr u32 num_threads = CACHING_NUMPAGES / 32; // 1 bit per page, 32 pages per workgroup
|
||||
constexpr u32 num_workgroups = Common::DivCeil(num_threads, 64u);
|
||||
cmdbuf.dispatch(num_workgroups, 1, 1);
|
||||
|
||||
// Reset fault buffer
|
||||
const vk::BufferMemoryBarrier2 reset_pre_barrier = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eShaderRead,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eTransferWrite,
|
||||
.buffer = fault_buffer.Handle(),
|
||||
.offset = 0,
|
||||
.size = FAULT_BUFFER_SIZE,
|
||||
};
|
||||
const vk::BufferMemoryBarrier2 reset_post_barrier = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite,
|
||||
.buffer = fault_buffer.Handle(),
|
||||
.offset = 0,
|
||||
.size = FAULT_BUFFER_SIZE,
|
||||
};
|
||||
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
|
||||
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
|
||||
.bufferMemoryBarrierCount = 1,
|
||||
.pBufferMemoryBarriers = &reset_pre_barrier,
|
||||
});
|
||||
cmdbuf.fillBuffer(fault_buffer.buffer, 0, FAULT_BUFFER_SIZE, 0);
|
||||
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
|
||||
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
|
||||
.bufferMemoryBarrierCount = 1,
|
||||
.pBufferMemoryBarriers = &reset_post_barrier,
|
||||
});
|
||||
|
||||
// Defer creating buffers
|
||||
scheduler.DeferOperation([this, mapped]() {
|
||||
// Create the fault buffers batched
|
||||
boost::icl::interval_set<VAddr> fault_ranges;
|
||||
const u64* fault_ptr = std::bit_cast<const u64*>(mapped);
|
||||
const u32 fault_count = static_cast<u32>(*(fault_ptr++));
|
||||
for (u32 i = 0; i < fault_count; ++i) {
|
||||
const VAddr fault = *(fault_ptr++);
|
||||
const VAddr fault_end = fault + CACHING_PAGESIZE; // This can be adjusted
|
||||
fault_ranges +=
|
||||
boost::icl::interval_set<VAddr>::interval_type::right_open(fault, fault_end);
|
||||
LOG_INFO(Render_Vulkan, "Accessed non-GPU cached memory at {:#x}", fault);
|
||||
}
|
||||
for (const auto& range : fault_ranges) {
|
||||
const VAddr start = range.lower();
|
||||
const VAddr end = range.upper();
|
||||
const u64 page_start = start >> CACHING_PAGEBITS;
|
||||
const u64 page_end = Common::DivCeil(end, CACHING_PAGESIZE);
|
||||
// Buffer size is in 32 bits
|
||||
ASSERT_MSG((range.upper() - range.lower()) <= std::numeric_limits<u32>::max(),
|
||||
"Buffer size is too large");
|
||||
CreateBuffer(start, static_cast<u32>(end - start));
|
||||
}
|
||||
});
|
||||
fault_manager.ProcessFaultBuffer();
|
||||
}
|
||||
|
||||
void BufferCache::Register(BufferId buffer_id) {
|
||||
@ -889,7 +685,7 @@ bool BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size,
|
||||
});
|
||||
TouchBuffer(buffer);
|
||||
}
|
||||
if (is_texel_buffer) {
|
||||
if (is_texel_buffer && !is_written) {
|
||||
return SynchronizeBufferFromImage(buffer, device_addr, size);
|
||||
}
|
||||
return false;
|
||||
@ -972,10 +768,7 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr,
|
||||
}
|
||||
|
||||
void BufferCache::SynchronizeBuffersInRange(VAddr device_addr, u64 size) {
|
||||
if (device_addr == 0) {
|
||||
return;
|
||||
}
|
||||
VAddr device_addr_end = device_addr + size;
|
||||
const VAddr device_addr_end = device_addr + size;
|
||||
ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
|
||||
RENDERER_TRACE;
|
||||
VAddr start = std::max(buffer.CpuAddr(), device_addr);
|
||||
@ -985,21 +778,6 @@ void BufferCache::SynchronizeBuffersInRange(VAddr device_addr, u64 size) {
|
||||
});
|
||||
}
|
||||
|
||||
void BufferCache::MemoryBarrier() {
|
||||
scheduler.EndRendering();
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
vk::MemoryBarrier2 barrier = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eMemoryWrite,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eMemoryRead,
|
||||
};
|
||||
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
|
||||
.memoryBarrierCount = 1,
|
||||
.pMemoryBarriers = &barrier,
|
||||
});
|
||||
}
|
||||
|
||||
void BufferCache::InlineDataBuffer(Buffer& buffer, VAddr address, const void* value,
|
||||
u32 num_bytes) {
|
||||
scheduler.EndRendering();
|
||||
|
||||
@ -3,12 +3,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <shared_mutex>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include "common/lru_cache.h"
|
||||
#include "common/slot_vector.h"
|
||||
#include "common/types.h"
|
||||
#include "video_core/buffer_cache/buffer.h"
|
||||
#include "video_core/buffer_cache/fault_manager.h"
|
||||
#include "video_core/buffer_cache/range_set.h"
|
||||
#include "video_core/multi_level_page_table.h"
|
||||
|
||||
@ -40,9 +40,7 @@ public:
|
||||
static constexpr u64 CACHING_PAGESIZE = u64{1} << CACHING_PAGEBITS;
|
||||
static constexpr u64 DEVICE_PAGESIZE = 16_KB;
|
||||
static constexpr u64 CACHING_NUMPAGES = u64{1} << (40 - CACHING_PAGEBITS);
|
||||
|
||||
static constexpr u64 BDA_PAGETABLE_SIZE = CACHING_NUMPAGES * sizeof(vk::DeviceAddress);
|
||||
static constexpr u64 FAULT_BUFFER_SIZE = CACHING_NUMPAGES / 8; // Bit per page
|
||||
|
||||
// Default values for garbage collection
|
||||
static constexpr s64 DEFAULT_TRIGGER_GC_MEMORY = 1_GB;
|
||||
@ -68,12 +66,6 @@ public:
|
||||
bool has_stream_leap = false;
|
||||
};
|
||||
|
||||
using IntervalSet =
|
||||
boost::icl::interval_set<VAddr, std::less,
|
||||
ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, VAddr, std::less),
|
||||
RangeSetsAllocator>;
|
||||
using IntervalType = typename IntervalSet::interval_type;
|
||||
|
||||
public:
|
||||
explicit BufferCache(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler,
|
||||
AmdGpu::Liverpool* liverpool, TextureCache& texture_cache,
|
||||
@ -92,7 +84,7 @@ public:
|
||||
|
||||
/// Retrieves the fault buffer.
|
||||
[[nodiscard]] Buffer* GetFaultBuffer() noexcept {
|
||||
return &fault_buffer;
|
||||
return fault_manager.GetFaultBuffer();
|
||||
}
|
||||
|
||||
/// Retrieves the buffer with the specified id.
|
||||
@ -160,9 +152,6 @@ public:
|
||||
/// Synchronizes all buffers neede for DMA.
|
||||
void SynchronizeDmaBuffers();
|
||||
|
||||
/// Record memory barrier. Used for buffers when accessed via BDA.
|
||||
void MemoryBarrier();
|
||||
|
||||
/// Runs the garbage collector.
|
||||
void RunGarbageCollector();
|
||||
|
||||
@ -217,6 +206,7 @@ private:
|
||||
AmdGpu::Liverpool* liverpool;
|
||||
Core::MemoryManager* memory;
|
||||
TextureCache& texture_cache;
|
||||
FaultManager fault_manager;
|
||||
std::unique_ptr<MemoryTracker> memory_tracker;
|
||||
StreamBuffer staging_buffer;
|
||||
StreamBuffer stream_buffer;
|
||||
@ -224,8 +214,6 @@ private:
|
||||
StreamBuffer device_buffer;
|
||||
Buffer gds_buffer;
|
||||
Buffer bda_pagetable_buffer;
|
||||
Buffer fault_buffer;
|
||||
std::shared_mutex slot_buffers_mutex;
|
||||
Common::SlotVector<Buffer> slot_buffers;
|
||||
u64 total_used_memory = 0;
|
||||
u64 trigger_gc_memory = 0;
|
||||
@ -235,9 +223,6 @@ private:
|
||||
RangeSet gpu_modified_ranges;
|
||||
SplitRangeMap<BufferId> buffer_ranges;
|
||||
PageTable page_table;
|
||||
vk::UniqueDescriptorSetLayout fault_process_desc_layout;
|
||||
vk::UniquePipeline fault_process_pipeline;
|
||||
vk::UniquePipelineLayout fault_process_pipeline_layout;
|
||||
};
|
||||
|
||||
} // namespace VideoCore
|
||||
|
||||
177
src/video_core/buffer_cache/fault_manager.cpp
Normal file
177
src/video_core/buffer_cache/fault_manager.cpp
Normal file
@ -0,0 +1,177 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/div_ceil.h"
|
||||
#include "video_core/buffer_cache/buffer_cache.h"
|
||||
#include "video_core/buffer_cache/fault_manager.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
#include "video_core/renderer_vulkan/vk_platform.h"
|
||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||
|
||||
#include "video_core/host_shaders/fault_buffer_process_comp.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
static constexpr size_t MaxPageFaults = 1024;
|
||||
static constexpr size_t PageFaultAreaSize = MaxPageFaults * sizeof(u64);
|
||||
|
||||
FaultManager::FaultManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler_,
|
||||
BufferCache& buffer_cache_, u32 caching_pagebits, u64 caching_num_pages_)
|
||||
: scheduler{scheduler_}, buffer_cache{buffer_cache_},
|
||||
caching_pagesize{1ULL << caching_pagebits}, caching_num_pages{caching_num_pages_},
|
||||
fault_buffer_size{caching_num_pages_ / 8},
|
||||
fault_buffer{instance, scheduler, MemoryUsage::DeviceLocal, 0, AllFlags, fault_buffer_size},
|
||||
download_buffer{instance, scheduler, MemoryUsage::Download,
|
||||
0, AllFlags, MaxPendingFaults * PageFaultAreaSize} {
|
||||
const auto device = instance.GetDevice();
|
||||
Vulkan::SetObjectName(device, fault_buffer.Handle(), "Fault Buffer");
|
||||
|
||||
const std::array<vk::DescriptorSetLayoutBinding, 2> bindings = {{
|
||||
{
|
||||
.binding = 0,
|
||||
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eCompute,
|
||||
},
|
||||
{
|
||||
.binding = 1,
|
||||
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eCompute,
|
||||
},
|
||||
}};
|
||||
const vk::DescriptorSetLayoutCreateInfo desc_layout_ci = {
|
||||
.flags = vk::DescriptorSetLayoutCreateFlagBits::ePushDescriptorKHR,
|
||||
.bindingCount = 2,
|
||||
.pBindings = bindings.data(),
|
||||
};
|
||||
fault_process_desc_layout =
|
||||
Vulkan::Check(device.createDescriptorSetLayoutUnique(desc_layout_ci));
|
||||
|
||||
std::vector<std::string> defines{{fmt::format("CACHING_PAGEBITS={}", caching_pagebits),
|
||||
fmt::format("MAX_PAGE_FAULTS={}", MaxPageFaults)}};
|
||||
const auto module = Vulkan::Compile(HostShaders::FAULT_BUFFER_PROCESS_COMP,
|
||||
vk::ShaderStageFlagBits::eCompute, device, defines);
|
||||
Vulkan::SetObjectName(device, module, "Fault Buffer Parser");
|
||||
|
||||
const vk::PipelineShaderStageCreateInfo shader_ci = {
|
||||
.stage = vk::ShaderStageFlagBits::eCompute,
|
||||
.module = module,
|
||||
.pName = "main",
|
||||
};
|
||||
|
||||
const vk::PipelineLayoutCreateInfo layout_info = {
|
||||
.setLayoutCount = 1U,
|
||||
.pSetLayouts = &(*fault_process_desc_layout),
|
||||
};
|
||||
fault_process_pipeline_layout = Vulkan::Check(device.createPipelineLayoutUnique(layout_info));
|
||||
|
||||
const vk::ComputePipelineCreateInfo pipeline_info = {
|
||||
.stage = shader_ci,
|
||||
.layout = *fault_process_pipeline_layout,
|
||||
};
|
||||
fault_process_pipeline = Vulkan::Check(device.createComputePipelineUnique({}, pipeline_info));
|
||||
Vulkan::SetObjectName(device, *fault_process_pipeline, "Fault Buffer Parser Pipeline");
|
||||
|
||||
device.destroyShaderModule(module);
|
||||
}
|
||||
|
||||
void FaultManager::ProcessFaultBuffer() {
|
||||
if (u64 wait_tick = fault_areas[current_area]) {
|
||||
scheduler.Wait(wait_tick);
|
||||
scheduler.PopPendingOperations();
|
||||
}
|
||||
|
||||
const u32 offset = current_area * PageFaultAreaSize;
|
||||
u8* mapped = download_buffer.mapped_data.data() + offset;
|
||||
std::memset(mapped, 0, PageFaultAreaSize);
|
||||
|
||||
const vk::BufferMemoryBarrier2 pre_barrier = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eShaderWrite,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eShaderRead,
|
||||
.buffer = fault_buffer.Handle(),
|
||||
.offset = 0,
|
||||
.size = fault_buffer_size,
|
||||
};
|
||||
const vk::BufferMemoryBarrier2 post_barrier = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eShaderWrite,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eShaderWrite,
|
||||
.buffer = fault_buffer.Handle(),
|
||||
.offset = 0,
|
||||
.size = fault_buffer_size,
|
||||
};
|
||||
const vk::DescriptorBufferInfo fault_buffer_info = {
|
||||
.buffer = fault_buffer.Handle(),
|
||||
.offset = 0,
|
||||
.range = fault_buffer_size,
|
||||
};
|
||||
const vk::DescriptorBufferInfo download_info = {
|
||||
.buffer = download_buffer.Handle(),
|
||||
.offset = offset,
|
||||
.range = PageFaultAreaSize,
|
||||
};
|
||||
const std::array<vk::WriteDescriptorSet, 2> writes = {{
|
||||
{
|
||||
.dstSet = VK_NULL_HANDLE,
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
||||
.pBufferInfo = &fault_buffer_info,
|
||||
},
|
||||
{
|
||||
.dstSet = VK_NULL_HANDLE,
|
||||
.dstBinding = 1,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
||||
.pBufferInfo = &download_info,
|
||||
},
|
||||
}};
|
||||
scheduler.EndRendering();
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
|
||||
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
|
||||
.bufferMemoryBarrierCount = 1,
|
||||
.pBufferMemoryBarriers = &pre_barrier,
|
||||
});
|
||||
cmdbuf.bindPipeline(vk::PipelineBindPoint::eCompute, *fault_process_pipeline);
|
||||
cmdbuf.pushDescriptorSetKHR(vk::PipelineBindPoint::eCompute, *fault_process_pipeline_layout, 0,
|
||||
writes);
|
||||
// 1 bit per page, 32 pages per workgroup
|
||||
const u32 num_threads = caching_num_pages / 32;
|
||||
const u32 num_workgroups = Common::DivCeil(num_threads, 64u);
|
||||
cmdbuf.dispatch(num_workgroups, 1, 1);
|
||||
|
||||
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
|
||||
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
|
||||
.bufferMemoryBarrierCount = 1,
|
||||
.pBufferMemoryBarriers = &post_barrier,
|
||||
});
|
||||
|
||||
scheduler.DeferOperation([this, mapped, area = current_area] {
|
||||
fault_ranges.Clear();
|
||||
const u64* fault_buf = std::bit_cast<const u64*>(mapped);
|
||||
const u32 fault_count = fault_buf[0];
|
||||
for (u32 i = 1; i <= fault_count; ++i) {
|
||||
fault_ranges.Add(fault_buf[i], caching_pagesize);
|
||||
LOG_INFO(Render_Vulkan, "Accessed non-GPU cached memory at {:#x}", fault_buf[i]);
|
||||
}
|
||||
fault_ranges.ForEach([&](VAddr start, VAddr end) {
|
||||
ASSERT_MSG((end - start) <= std::numeric_limits<u32>::max(),
|
||||
"Buffer size is too large");
|
||||
buffer_cache.FindBuffer(start, static_cast<u32>(end - start));
|
||||
});
|
||||
fault_areas[area] = 0;
|
||||
});
|
||||
|
||||
fault_areas[current_area++] = scheduler.CurrentTick();
|
||||
current_area %= MaxPendingFaults;
|
||||
}
|
||||
|
||||
} // namespace VideoCore
|
||||
42
src/video_core/buffer_cache/fault_manager.h
Normal file
42
src/video_core/buffer_cache/fault_manager.h
Normal file
@ -0,0 +1,42 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/buffer_cache/buffer.h"
|
||||
#include "video_core/buffer_cache/range_set.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
class BufferCache;
|
||||
|
||||
class FaultManager {
|
||||
static constexpr size_t MaxPendingFaults = 8;
|
||||
|
||||
public:
|
||||
explicit FaultManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler,
|
||||
BufferCache& buffer_cache, u32 caching_pagebits, u64 caching_num_pages);
|
||||
|
||||
[[nodiscard]] Buffer* GetFaultBuffer() noexcept {
|
||||
return &fault_buffer;
|
||||
}
|
||||
|
||||
void ProcessFaultBuffer();
|
||||
|
||||
private:
|
||||
Vulkan::Scheduler& scheduler;
|
||||
BufferCache& buffer_cache;
|
||||
RangeSet fault_ranges;
|
||||
u64 caching_pagesize;
|
||||
u64 caching_num_pages;
|
||||
u64 fault_buffer_size;
|
||||
Buffer fault_buffer;
|
||||
Buffer download_buffer;
|
||||
std::array<u64, MaxPendingFaults> fault_areas{};
|
||||
u32 current_area{};
|
||||
vk::UniqueDescriptorSetLayout fault_process_desc_layout;
|
||||
vk::UniquePipeline fault_process_pipeline;
|
||||
vk::UniquePipelineLayout fault_process_pipeline_layout;
|
||||
};
|
||||
|
||||
} // namespace VideoCore
|
||||
@ -13,30 +13,23 @@ layout(std430, binding = 0) buffer input_buf {
|
||||
layout(std430, binding = 1) buffer output_buf {
|
||||
uint64_t download_buffer[];
|
||||
};
|
||||
|
||||
// Overlap for 32 bit atomics
|
||||
layout(std430, binding = 1) buffer output_buf32 {
|
||||
uint download_buffer32[];
|
||||
};
|
||||
|
||||
layout(constant_id = 0) const uint CACHING_PAGEBITS = 0;
|
||||
|
||||
void main() {
|
||||
uint id = gl_GlobalInvocationID.x;
|
||||
const uint id = gl_GlobalInvocationID.x;
|
||||
uint word = fault_buffer[id];
|
||||
if (word == 0u) {
|
||||
return;
|
||||
}
|
||||
// 1 page per bit
|
||||
uint base_bit = id * 32u;
|
||||
fault_buffer[id] = 0u;
|
||||
const uint base_bit = id * 32u;
|
||||
while (word != 0u) {
|
||||
uint bit = findLSB(word);
|
||||
word &= word - 1;
|
||||
uint page = base_bit + bit;
|
||||
uint store_index = atomicAdd(download_buffer32[0], 1u) + 1u;
|
||||
// It is very unlikely, but should we check for overflow?
|
||||
if (store_index < 1024u) { // only support 1024 page faults
|
||||
download_buffer[store_index] = uint64_t(page) << CACHING_PAGEBITS;
|
||||
const uint store_index = atomicAdd(download_buffer32[0], 1u) + 1u;
|
||||
if (store_index >= MAX_PAGE_FAULTS) {
|
||||
return;
|
||||
}
|
||||
const uint bit = findLSB(word);
|
||||
word &= word - 1;
|
||||
const uint page = base_bit + bit;
|
||||
download_buffer[store_index] = uint64_t(page) << CACHING_PAGEBITS;
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,13 +355,12 @@ bool PipelineCache::RefreshGraphicsKey() {
|
||||
}
|
||||
|
||||
// Fill color target information
|
||||
key.color_buffers[cb] = Shader::PsColorBuffer{
|
||||
.data_format = col_buf.GetDataFmt(),
|
||||
.num_format = col_buf.GetNumberFmt(),
|
||||
.num_conversion = col_buf.GetNumberConversion(),
|
||||
.export_format = regs.color_export_format.GetFormat(cb),
|
||||
.swizzle = col_buf.Swizzle(),
|
||||
};
|
||||
auto& color_buffer = key.color_buffers[cb];
|
||||
color_buffer.data_format = col_buf.GetDataFmt();
|
||||
color_buffer.num_format = col_buf.GetNumberFmt();
|
||||
color_buffer.num_conversion = col_buf.GetNumberConversion();
|
||||
color_buffer.export_format = regs.color_export_format.GetFormat(cb);
|
||||
color_buffer.swizzle = col_buf.Swizzle();
|
||||
}
|
||||
|
||||
// Compile and bind shader stages
|
||||
@ -379,7 +378,7 @@ bool PipelineCache::RefreshGraphicsKey() {
|
||||
continue;
|
||||
}
|
||||
if ((key.mrt_mask & (1u << cb)) == 0) {
|
||||
key.color_buffers[cb] = {};
|
||||
std::memset(&key.color_buffers[cb], 0, sizeof(Shader::PsColorBuffer));
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -632,7 +631,7 @@ void PipelineCache::DumpShader(std::span<const u32> code, u64 hash, Shader::Stag
|
||||
std::filesystem::create_directories(dump_dir);
|
||||
}
|
||||
const auto filename = fmt::format("{}.{}", GetShaderName(stage, hash, perm_idx), ext);
|
||||
const auto file = IOFile{dump_dir / filename, FileAccessMode::Write};
|
||||
const auto file = IOFile{dump_dir / filename, FileAccessMode::Create};
|
||||
file.WriteSpan(code);
|
||||
}
|
||||
|
||||
|
||||
@ -380,7 +380,8 @@ void Rasterizer::OnSubmit() {
|
||||
}
|
||||
|
||||
bool Rasterizer::BindResources(const Pipeline* pipeline) {
|
||||
if (IsComputeImageCopy(pipeline) || IsComputeMetaClear(pipeline)) {
|
||||
if (IsComputeImageCopy(pipeline) || IsComputeMetaClear(pipeline) ||
|
||||
IsComputeImageClear(pipeline)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -406,18 +407,13 @@ bool Rasterizer::BindResources(const Pipeline* pipeline) {
|
||||
|
||||
if (uses_dma) {
|
||||
// We only use fault buffer for DMA right now.
|
||||
{
|
||||
Common::RecursiveSharedLock lock{mapped_ranges_mutex};
|
||||
for (auto& range : mapped_ranges) {
|
||||
buffer_cache.SynchronizeBuffersInRange(range.lower(),
|
||||
range.upper() - range.lower());
|
||||
}
|
||||
Common::RecursiveSharedLock lock{mapped_ranges_mutex};
|
||||
for (auto& range : mapped_ranges) {
|
||||
buffer_cache.SynchronizeBuffersInRange(range.lower(), range.upper() - range.lower());
|
||||
}
|
||||
buffer_cache.MemoryBarrier();
|
||||
fault_process_pending = true;
|
||||
}
|
||||
|
||||
fault_process_pending |= uses_dma;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -520,6 +516,66 @@ bool Rasterizer::IsComputeImageCopy(const Pipeline* pipeline) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Rasterizer::IsComputeImageClear(const Pipeline* pipeline) {
|
||||
if (!pipeline->IsCompute()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure shader only has 2 bound buffers
|
||||
const auto& cs_pgm = liverpool->GetCsRegs();
|
||||
const auto& info = pipeline->GetStage(Shader::LogicalStage::Compute);
|
||||
if (cs_pgm.num_thread_x.full != 64 || info.buffers.size() != 2 || !info.images.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// From those 2 buffers, first must hold the clear vector and second the image being cleared
|
||||
const auto& desc0 = info.buffers[0];
|
||||
const auto& desc1 = info.buffers[1];
|
||||
if (desc0.is_formatted || !desc1.is_formatted || desc0.is_written || !desc1.is_written) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// First buffer must have size of vec4 and second the size of a single layer
|
||||
const AmdGpu::Buffer buf0 = desc0.GetSharp(info);
|
||||
const AmdGpu::Buffer buf1 = desc1.GetSharp(info);
|
||||
const u32 buf1_bpp = AmdGpu::NumBitsPerBlock(buf1.GetDataFmt());
|
||||
if (buf0.GetSize() != 16 || (cs_pgm.dim_x * 128ULL * (buf1_bpp / 8)) != buf1.GetSize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find image the buffer alias
|
||||
const auto image1_id =
|
||||
texture_cache.FindImageFromRange(buf1.base_address, buf1.GetSize(), false);
|
||||
if (!image1_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Image clear must be valid
|
||||
VideoCore::Image& image1 = texture_cache.GetImage(image1_id);
|
||||
if (image1.info.guest_size != buf1.GetSize() || image1.info.num_bits != buf1_bpp ||
|
||||
image1.info.props.is_depth) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform image clear
|
||||
const float* values = reinterpret_cast<float*>(buf0.base_address);
|
||||
const vk::ClearValue clear = {
|
||||
.color = {.float32 = std::array<float, 4>{values[0], values[1], values[2], values[3]}},
|
||||
};
|
||||
const VideoCore::SubresourceRange range = {
|
||||
.base =
|
||||
{
|
||||
.level = 0,
|
||||
.layer = 0,
|
||||
},
|
||||
.extent = image1.info.resources,
|
||||
};
|
||||
image1.Clear(clear, range);
|
||||
image1.flags |= VideoCore::ImageFlagBits::GpuModified;
|
||||
image1.flags &= ~VideoCore::ImageFlagBits::Dirty;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Bindings& binding,
|
||||
Shader::PushData& push_data) {
|
||||
buffer_bindings.clear();
|
||||
|
||||
@ -112,6 +112,7 @@ private:
|
||||
|
||||
bool IsComputeMetaClear(const Pipeline* pipeline);
|
||||
bool IsComputeImageCopy(const Pipeline* pipeline);
|
||||
bool IsComputeImageClear(const Pipeline* pipeline);
|
||||
|
||||
private:
|
||||
friend class VideoCore::BufferCache;
|
||||
|
||||
@ -84,15 +84,6 @@ void Scheduler::Wait(u64 tick) {
|
||||
Flush(info);
|
||||
}
|
||||
master_semaphore.Wait(tick);
|
||||
|
||||
// CAUTION: This can introduce unexpected variation in the wait time.
|
||||
// We don't currently sync the GPU, and some games are very sensitive to this.
|
||||
// If this becomes a problem, it can be commented out.
|
||||
// Idealy we would implement proper gpu sync.
|
||||
while (!pending_ops.empty() && pending_ops.front().gpu_tick <= tick) {
|
||||
pending_ops.front().callback();
|
||||
pending_ops.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::PopPendingOperations() {
|
||||
@ -109,9 +100,7 @@ void Scheduler::AllocateWorkerCommandBuffers() {
|
||||
};
|
||||
|
||||
current_cmdbuf = command_pool.Commit();
|
||||
auto begin_result = current_cmdbuf.begin(begin_info);
|
||||
ASSERT_MSG(begin_result == vk::Result::eSuccess, "Failed to begin command buffer: {}",
|
||||
vk::to_string(begin_result));
|
||||
Check(current_cmdbuf.begin(begin_info));
|
||||
|
||||
// Invalidate dynamic state so it gets applied to the new command buffer.
|
||||
dynamic_state.Invalidate();
|
||||
@ -139,9 +128,7 @@ void Scheduler::SubmitExecution(SubmitInfo& info) {
|
||||
#endif
|
||||
|
||||
EndRendering();
|
||||
auto end_result = current_cmdbuf.end();
|
||||
ASSERT_MSG(end_result == vk::Result::eSuccess, "Failed to end command buffer: {}",
|
||||
vk::to_string(end_result));
|
||||
Check(current_cmdbuf.end());
|
||||
|
||||
const vk::Semaphore timeline = master_semaphore.Handle();
|
||||
info.AddSignal(timeline, signal_value);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user