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
|
branch = dist
|
||||||
[submodule "externals/MoltenVK"]
|
[submodule "externals/MoltenVK"]
|
||||||
path = externals/MoltenVK
|
path = externals/MoltenVK
|
||||||
url = https://github.com/KhronosGroup/MoltenVK.git
|
url = https://github.com/shadPS4-emu/ext-MoltenVK.git
|
||||||
shallow = true
|
shallow = true
|
||||||
[submodule "externals/json"]
|
[submodule "externals/json"]
|
||||||
path = externals/json
|
path = externals/json
|
||||||
url = https://github.com/nlohmann/json.git
|
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 Version
|
||||||
set(EMULATOR_VERSION_MAJOR "0")
|
set(EMULATOR_VERSION_MAJOR "0")
|
||||||
set(EMULATOR_VERSION_MINOR "12")
|
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}")
|
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(PNG 1.6 MODULE)
|
||||||
find_package(RenderDoc 1.6.0 MODULE)
|
find_package(RenderDoc 1.6.0 MODULE)
|
||||||
find_package(SDL3 3.1.2 CONFIG)
|
find_package(SDL3 3.1.2 CONFIG)
|
||||||
|
find_package(SDL3_mixer 2.8.1 CONFIG)
|
||||||
find_package(stb MODULE)
|
find_package(stb MODULE)
|
||||||
find_package(toml11 4.2.0 CONFIG)
|
find_package(toml11 4.2.0 CONFIG)
|
||||||
find_package(tsl-robin-map 1.3.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/mouse/mouse.h
|
||||||
src/core/libraries/web_browser_dialog/webbrowserdialog.cpp
|
src/core/libraries/web_browser_dialog/webbrowserdialog.cpp
|
||||||
src/core/libraries/web_browser_dialog/webbrowserdialog.h
|
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
|
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
|
set(USBD_LIB src/core/libraries/usbd/usbd.cpp
|
||||||
src/core/libraries/usbd/usbd.h
|
src/core/libraries/usbd/usbd.h
|
||||||
src/core/libraries/usbd/usb_backend.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.cpp
|
||||||
src/core/libraries/usbd/emulated/skylander.h
|
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.h
|
||||||
src/video_core/buffer_cache/buffer_cache.cpp
|
src/video_core/buffer_cache/buffer_cache.cpp
|
||||||
src/video_core/buffer_cache/buffer_cache.h
|
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/memory_tracker.h
|
||||||
src/video_core/buffer_cache/range_set.h
|
src/video_core/buffer_cache/range_set.h
|
||||||
src/video_core/buffer_cache/region_definitions.h
|
src/video_core/buffer_cache/region_definitions.h
|
||||||
@ -1062,7 +1075,7 @@ add_executable(shadps4
|
|||||||
create_target_directory_groups(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 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(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")
|
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>
|
<category translate="no">Game</category>
|
||||||
</categories>
|
</categories>
|
||||||
<releases>
|
<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">
|
<release version="0.12.0" date="2025-10-31">
|
||||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.12.0</url>
|
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.12.0</url>
|
||||||
</release>
|
</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`
|
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.
|
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.
|
3. Change the project to build to shadps4.exe
|
||||||
4. Change the project to build to shadps4.exe
|
4. Build -> Build All
|
||||||
5. Build -> Build All
|
|
||||||
|
|
||||||
Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\`
|
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
|
## Option 2: MSYS2/MinGW
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
@ -73,7 +68,6 @@ ARM64-based computers, follow:
|
|||||||
1. Open "MSYS2 CLANGARM64" from your new applications
|
1. Open "MSYS2 CLANGARM64" from your new applications
|
||||||
2. Run `pacman -Syu`, let it complete;
|
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`
|
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`
|
4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4`
|
||||||
5. Run `cd 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"`
|
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)
|
add_subdirectory(sdl3)
|
||||||
endif()
|
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
|
# vulkan-headers
|
||||||
if (NOT TARGET Vulkan::Headers)
|
if (NOT TARGET Vulkan::Headers)
|
||||||
set(VULKAN_HEADERS_ENABLE_MODULE OFF)
|
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 {
|
pkgs.mkShell {
|
||||||
name = "shadps4-build-env";
|
name = "shadps4-build-env";
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = with pkgs; [
|
||||||
pkgs.llvmPackages_18.clang
|
llvmPackages_18.clang
|
||||||
pkgs.cmake
|
cmake
|
||||||
pkgs.pkg-config
|
pkg-config
|
||||||
pkgs.git
|
git
|
||||||
|
util-linux
|
||||||
];
|
];
|
||||||
|
|
||||||
buildInputs = [
|
buildInputs = with pkgs; [
|
||||||
pkgs.alsa-lib
|
alsa-lib
|
||||||
pkgs.libpulseaudio
|
libpulseaudio
|
||||||
pkgs.openal
|
openal
|
||||||
pkgs.zlib
|
zlib
|
||||||
pkgs.libedit
|
libedit
|
||||||
pkgs.udev
|
udev
|
||||||
pkgs.libevdev
|
libevdev
|
||||||
pkgs.SDL2
|
SDL2
|
||||||
pkgs.jack2
|
jack2
|
||||||
pkgs.sndio
|
sndio
|
||||||
|
|
||||||
pkgs.vulkan-headers
|
vulkan-headers
|
||||||
pkgs.vulkan-utility-libraries
|
vulkan-utility-libraries
|
||||||
pkgs.vulkan-tools
|
vulkan-tools
|
||||||
|
|
||||||
pkgs.ffmpeg
|
ffmpeg
|
||||||
pkgs.fmt
|
fmt
|
||||||
pkgs.glslang
|
glslang
|
||||||
pkgs.libxkbcommon
|
libxkbcommon
|
||||||
pkgs.wayland
|
wayland
|
||||||
pkgs.xorg.libxcb
|
xorg.libxcb
|
||||||
pkgs.xorg.xcbutil
|
xorg.xcbutil
|
||||||
pkgs.xorg.xcbutilkeysyms
|
xorg.xcbutilkeysyms
|
||||||
pkgs.xorg.xcbutilwm
|
xorg.xcbutilwm
|
||||||
pkgs.sdl3
|
sdl3
|
||||||
pkgs.stb
|
stb
|
||||||
pkgs.wayland-protocols
|
wayland-protocols
|
||||||
pkgs.libpng
|
libpng
|
||||||
];
|
];
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
|
|||||||
@ -68,6 +68,7 @@ class ElfInfo {
|
|||||||
std::string app_ver{};
|
std::string app_ver{};
|
||||||
u32 firmware_ver = 0;
|
u32 firmware_ver = 0;
|
||||||
u32 raw_firmware_ver = 0;
|
u32 raw_firmware_ver = 0;
|
||||||
|
u32 sdk_ver = 0;
|
||||||
PSFAttributes psf_attributes{};
|
PSFAttributes psf_attributes{};
|
||||||
|
|
||||||
std::filesystem::path splash_path{};
|
std::filesystem::path splash_path{};
|
||||||
@ -117,6 +118,11 @@ public:
|
|||||||
return raw_firmware_ver;
|
return raw_firmware_ver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u32 CompiledSdkVer() const {
|
||||||
|
ASSERT(initialized);
|
||||||
|
return sdk_ver;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] const PSFAttributes& GetPSFAttributes() const {
|
[[nodiscard]] const PSFAttributes& GetPSFAttributes() const {
|
||||||
ASSERT(initialized);
|
ASSERT(initialized);
|
||||||
return psf_attributes;
|
return psf_attributes;
|
||||||
|
|||||||
@ -40,28 +40,30 @@ namespace {
|
|||||||
switch (mode) {
|
switch (mode) {
|
||||||
case FileAccessMode::Read:
|
case FileAccessMode::Read:
|
||||||
return L"rb";
|
return L"rb";
|
||||||
case FileAccessMode::Write:
|
|
||||||
return L"wb";
|
|
||||||
case FileAccessMode::Append:
|
case FileAccessMode::Append:
|
||||||
return L"ab";
|
return L"ab";
|
||||||
|
case FileAccessMode::Write:
|
||||||
case FileAccessMode::ReadWrite:
|
case FileAccessMode::ReadWrite:
|
||||||
return L"r+b";
|
return L"r+b";
|
||||||
case FileAccessMode::ReadAppend:
|
case FileAccessMode::ReadAppend:
|
||||||
return L"a+b";
|
return L"a+b";
|
||||||
|
case FileAccessMode::Create:
|
||||||
|
return L"wb";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FileType::TextFile:
|
case FileType::TextFile:
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case FileAccessMode::Read:
|
case FileAccessMode::Read:
|
||||||
return L"r";
|
return L"r";
|
||||||
case FileAccessMode::Write:
|
|
||||||
return L"w";
|
|
||||||
case FileAccessMode::Append:
|
case FileAccessMode::Append:
|
||||||
return L"a";
|
return L"a";
|
||||||
|
case FileAccessMode::Write:
|
||||||
case FileAccessMode::ReadWrite:
|
case FileAccessMode::ReadWrite:
|
||||||
return L"r+";
|
return L"r+";
|
||||||
case FileAccessMode::ReadAppend:
|
case FileAccessMode::ReadAppend:
|
||||||
return L"a+";
|
return L"a+";
|
||||||
|
case FileAccessMode::Create:
|
||||||
|
return L"w";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -91,28 +93,30 @@ namespace {
|
|||||||
switch (mode) {
|
switch (mode) {
|
||||||
case FileAccessMode::Read:
|
case FileAccessMode::Read:
|
||||||
return "rb";
|
return "rb";
|
||||||
case FileAccessMode::Write:
|
|
||||||
return "wb";
|
|
||||||
case FileAccessMode::Append:
|
case FileAccessMode::Append:
|
||||||
return "ab";
|
return "ab";
|
||||||
|
case FileAccessMode::Write:
|
||||||
case FileAccessMode::ReadWrite:
|
case FileAccessMode::ReadWrite:
|
||||||
return "r+b";
|
return "r+b";
|
||||||
case FileAccessMode::ReadAppend:
|
case FileAccessMode::ReadAppend:
|
||||||
return "a+b";
|
return "a+b";
|
||||||
|
case FileAccessMode::Create:
|
||||||
|
return "wb";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FileType::TextFile:
|
case FileType::TextFile:
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case FileAccessMode::Read:
|
case FileAccessMode::Read:
|
||||||
return "r";
|
return "r";
|
||||||
case FileAccessMode::Write:
|
|
||||||
return "w";
|
|
||||||
case FileAccessMode::Append:
|
case FileAccessMode::Append:
|
||||||
return "a";
|
return "a";
|
||||||
|
case FileAccessMode::Write:
|
||||||
case FileAccessMode::ReadWrite:
|
case FileAccessMode::ReadWrite:
|
||||||
return "r+";
|
return "r+";
|
||||||
case FileAccessMode::ReadAppend:
|
case FileAccessMode::ReadAppend:
|
||||||
return "a+";
|
return "a+";
|
||||||
|
case FileAccessMode::Create:
|
||||||
|
return "w";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,9 +21,8 @@ enum class FileAccessMode {
|
|||||||
*/
|
*/
|
||||||
Read = 1 << 0,
|
Read = 1 << 0,
|
||||||
/**
|
/**
|
||||||
* If the file at path exists, the existing contents of the file are erased.
|
* If the file at path exists, it opens the file for writing.
|
||||||
* The empty file is then opened for writing.
|
* If the file at path does not exist, it fails to open the file.
|
||||||
* If the file at path does not exist, it creates and opens a new empty file for writing.
|
|
||||||
*/
|
*/
|
||||||
Write = 1 << 1,
|
Write = 1 << 1,
|
||||||
/**
|
/**
|
||||||
@ -42,6 +41,12 @@ enum class FileAccessMode {
|
|||||||
* reading and appending.
|
* reading and appending.
|
||||||
*/
|
*/
|
||||||
ReadAppend = Read | Append,
|
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);
|
DECLARE_ENUM_FLAG_OPERATORS(FileAccessMode);
|
||||||
|
|
||||||
@ -102,6 +107,11 @@ public:
|
|||||||
return file != nullptr;
|
return file != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsWriteOnly() const {
|
||||||
|
return file_access_mode == FileAccessMode::Append ||
|
||||||
|
file_access_mode == FileAccessMode::Write;
|
||||||
|
}
|
||||||
|
|
||||||
uintptr_t GetFileMapping();
|
uintptr_t GetFileMapping();
|
||||||
|
|
||||||
int Open(const std::filesystem::path& path, FileAccessMode mode,
|
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) {
|
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);
|
return out.Write(data);
|
||||||
}
|
}
|
||||||
std::FILE* file = nullptr;
|
std::FILE* file = nullptr;
|
||||||
|
|||||||
@ -62,7 +62,7 @@ private:
|
|||||||
class FileBackend {
|
class FileBackend {
|
||||||
public:
|
public:
|
||||||
explicit FileBackend(const std::filesystem::path& filename, bool should_append = false)
|
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} {}
|
FS::FileType::TextFile} {}
|
||||||
|
|
||||||
~FileBackend() = default;
|
~FileBackend() = default;
|
||||||
@ -182,7 +182,13 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
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
|
// Propagate important log messages to the profiler
|
||||||
if (IsProfilerConnected()) {
|
if (IsProfilerConnected()) {
|
||||||
const auto& msg_str = fmt::format("[{}] {}", GetLogClassName(log_class), message);
|
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::duration_cast;
|
||||||
using std::chrono::microseconds;
|
using std::chrono::microseconds;
|
||||||
using std::chrono::steady_clock;
|
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,
|
unsigned int line_num, const char* function, const char* format,
|
||||||
const fmt::format_args& args) {
|
const fmt::format_args& args) {
|
||||||
if (!initialization_in_progress_suppress_logging) [[likely]] {
|
if (!initialization_in_progress_suppress_logging) [[likely]] {
|
||||||
Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function,
|
Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, format,
|
||||||
fmt::vformat(format, args));
|
args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace Common::Log
|
} // namespace Common::Log
|
||||||
|
|||||||
@ -140,6 +140,8 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
|||||||
SUB(Lib, NpParty) \
|
SUB(Lib, NpParty) \
|
||||||
SUB(Lib, Zlib) \
|
SUB(Lib, Zlib) \
|
||||||
SUB(Lib, Hmd) \
|
SUB(Lib, Hmd) \
|
||||||
|
SUB(Lib, Font) \
|
||||||
|
SUB(Lib, FontFt) \
|
||||||
SUB(Lib, HmdSetupDialog) \
|
SUB(Lib, HmdSetupDialog) \
|
||||||
SUB(Lib, SigninDialog) \
|
SUB(Lib, SigninDialog) \
|
||||||
SUB(Lib, Camera) \
|
SUB(Lib, Camera) \
|
||||||
|
|||||||
@ -114,6 +114,8 @@ enum class Class : u8 {
|
|||||||
Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation.
|
Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation.
|
||||||
Lib_CompanionUtil, ///< The LibCompanionUtil implementation.
|
Lib_CompanionUtil, ///< The LibCompanionUtil implementation.
|
||||||
Lib_VrTracker, ///< The LibSceVrTracker implementation.
|
Lib_VrTracker, ///< The LibSceVrTracker implementation.
|
||||||
|
Lib_Font, ///< The libSceFont implementation.
|
||||||
|
Lib_FontFt, ///< The libSceFontFt implementation.
|
||||||
Frontend, ///< Emulator UI
|
Frontend, ///< Emulator UI
|
||||||
Render, ///< Video Core
|
Render, ///< Video Core
|
||||||
Render_Vulkan, ///< Vulkan backend
|
Render_Vulkan, ///< Vulkan backend
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include "common/arch.h"
|
#include "common/arch.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
|
#include "common/elf_info.h"
|
||||||
#include "common/error.h"
|
#include "common/error.h"
|
||||||
#include "core/address_space.h"
|
#include "core/address_space.h"
|
||||||
#include "core/libraries/kernel/memory.h"
|
#include "core/libraries/kernel/memory.h"
|
||||||
@ -103,8 +104,8 @@ struct AddressSpace::Impl {
|
|||||||
GetSystemInfo(&sys_info);
|
GetSystemInfo(&sys_info);
|
||||||
u64 alignment = sys_info.dwAllocationGranularity;
|
u64 alignment = sys_info.dwAllocationGranularity;
|
||||||
|
|
||||||
// Determine the host OS build number
|
// Older Windows builds have a severe performance issue with VirtualAlloc2.
|
||||||
// Retrieve module handle for ntdll
|
// We need to get the host's Windows version, then determine if it needs a workaround.
|
||||||
auto ntdll_handle = GetModuleHandleW(L"ntdll.dll");
|
auto ntdll_handle = GetModuleHandleW(L"ntdll.dll");
|
||||||
ASSERT_MSG(ntdll_handle, "Failed to retrieve ntdll handle");
|
ASSERT_MSG(ntdll_handle, "Failed to retrieve ntdll handle");
|
||||||
|
|
||||||
@ -120,12 +121,20 @@ struct AddressSpace::Impl {
|
|||||||
u64 supported_user_max = USER_MAX;
|
u64 supported_user_max = USER_MAX;
|
||||||
// This is the build number for Windows 11 22H2
|
// This is the build number for Windows 11 22H2
|
||||||
static constexpr s32 AffectedBuildNumber = 22621;
|
static constexpr s32 AffectedBuildNumber = 22621;
|
||||||
if (os_version_info.dwBuildNumber <= AffectedBuildNumber) {
|
|
||||||
// Older Windows builds have an issue with VirtualAlloc2 on higher addresses.
|
// Higher PS4 firmware versions prevent higher address mappings too.
|
||||||
// To prevent regressions, limit the maximum address we reserve for this platform.
|
s32 sdk_ver = Common::ElfInfo::Instance().CompiledSdkVer();
|
||||||
supported_user_max = 0x11000000000ULL;
|
if (os_version_info.dwBuildNumber <= AffectedBuildNumber ||
|
||||||
LOG_WARNING(Core, "Windows 10 detected, reducing user max to {:#x} to avoid problems",
|
sdk_ver >= Common::ElfInfo::FW_30) {
|
||||||
supported_user_max);
|
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.
|
// Determine the free address ranges we can access.
|
||||||
|
|||||||
@ -152,7 +152,7 @@ inline std::string RunDisassembler(const std::string& disassembler_cli, const T&
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\"");
|
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.Write(shader_code);
|
||||||
file.Close();
|
file.Close();
|
||||||
|
|
||||||
|
|||||||
@ -123,7 +123,7 @@ void FrameDumpViewer::Draw() {
|
|||||||
const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time,
|
const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time,
|
||||||
magic_enum::enum_name(selected_queue_type),
|
magic_enum::enum_name(selected_queue_type),
|
||||||
selected_submit_num, selected_queue_num2);
|
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;
|
const auto& data = frame_dump->queues[selected_cmd].data;
|
||||||
if (file.IsOpen()) {
|
if (file.IsOpen()) {
|
||||||
DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname));
|
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 {
|
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()) {
|
if (!file.IsOpen()) {
|
||||||
return false;
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
// Create a file if it doesn't exist
|
// 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) {
|
} else if (!exists) {
|
||||||
// If we're not creating a file, and it doesn't exist, return ENOENT
|
// 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) {
|
if (read) {
|
||||||
// Read only
|
// Open exclusively for reading
|
||||||
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read);
|
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read);
|
||||||
} else if (read_only) {
|
} else if (read_only) {
|
||||||
// Can't open files with write/read-write access in a read only directory
|
// Can't open files with write/read-write access in a read only directory
|
||||||
h->DeleteHandle(handle);
|
h->DeleteHandle(handle);
|
||||||
*__Error() = POSIX_EROFS;
|
*__Error() = POSIX_EROFS;
|
||||||
return -1;
|
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) {
|
} else if (write) {
|
||||||
// Write only
|
if (append) {
|
||||||
e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write);
|
// 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) {
|
} else if (rdwr) {
|
||||||
// Read and write
|
// 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (file->f.IsWriteOnly()) {
|
||||||
|
*__Error() = POSIX_EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
s64 total_read = 0;
|
s64 total_read = 0;
|
||||||
for (s32 i = 0; i < iovcnt; i++) {
|
for (s32 i = 0; i < iovcnt; i++) {
|
||||||
total_read += ReadFile(file->f, iov[i].iov_base, iov[i].iov_len);
|
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.
|
// Socket functions handle errnos internally.
|
||||||
return file->socket->ReceivePacket(buf, nbytes, 0, nullptr, 0);
|
return file->socket->ReceivePacket(buf, nbytes, 0, nullptr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (file->f.IsWriteOnly()) {
|
||||||
|
*__Error() = POSIX_EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return ReadFile(file->f, buf, nbytes);
|
return ReadFile(file->f, buf, nbytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -620,17 +640,29 @@ s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) {
|
|||||||
*__Error() = POSIX_ENOENT;
|
*__Error() = POSIX_ENOENT;
|
||||||
return -1;
|
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)) {
|
if (std::filesystem::is_directory(path_name)) {
|
||||||
sb->st_mode = 0000777u | 0040000u;
|
sb->st_mode = 0000777u | 0040000u;
|
||||||
sb->st_size = 65536;
|
sb->st_size = 65536;
|
||||||
sb->st_blksize = 65536;
|
sb->st_blksize = 65536;
|
||||||
sb->st_blocks = 128;
|
sb->st_blocks = 128;
|
||||||
|
sb->st_mtim.tv_sec =
|
||||||
|
std::chrono::duration_cast<std::chrono::seconds>(mtimestamp.time_since_epoch()).count();
|
||||||
// TODO incomplete
|
// TODO incomplete
|
||||||
} else {
|
} else {
|
||||||
sb->st_mode = 0000777u | 0100000u;
|
sb->st_mode = 0000777u | 0100000u;
|
||||||
sb->st_size = static_cast<s64>(std::filesystem::file_size(path_name));
|
sb->st_size = static_cast<s64>(std::filesystem::file_size(path_name));
|
||||||
sb->st_blksize = 512;
|
sb->st_blksize = 512;
|
||||||
sb->st_blocks = (sb->st_size + 511) / 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
|
// 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* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||||
auto file = h->GetFile(src_path);
|
auto file = h->GetFile(src_path);
|
||||||
if (file) {
|
if (file) {
|
||||||
// We need to force ReadWrite if the file had Write access before
|
auto access_mode = file->f.GetAccessMode();
|
||||||
// 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();
|
|
||||||
file->f.Close();
|
file->f.Close();
|
||||||
std::filesystem::remove(src_path);
|
std::filesystem::remove(src_path);
|
||||||
file->f.Open(dst_path, access_mode);
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (file->f.IsWriteOnly()) {
|
||||||
|
*__Error() = POSIX_EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
const s64 pos = file->f.Tell();
|
const s64 pos = file->f.Tell();
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
file->f.Seek(pos);
|
file->f.Seek(pos);
|
||||||
|
|||||||
@ -236,15 +236,24 @@ s32 PS4_SYSV_ABI sceKernelSetGPO() {
|
|||||||
return ORBIS_OK;
|
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) {
|
s32 PS4_SYSV_ABI sceKernelGetSystemSwVersion(SwVersionStruct* ret) {
|
||||||
if (ret == nullptr) {
|
if (ret == nullptr) {
|
||||||
return ORBIS_OK; // but why?
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
ASSERT(ret->struct_size == 40);
|
u32 fake_fw = CURRENT_FIRMWARE_VERSION;
|
||||||
u32 fake_fw = Common::ElfInfo::Instance().RawFirmwareVer();
|
|
||||||
ret->hex_representation = fake_fw;
|
ret->hex_representation = fake_fw;
|
||||||
std::snprintf(ret->text_representation, 28, "%2x.%03x.%03x", fake_fw >> 0x18,
|
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);
|
LOG_INFO(Lib_Kernel, "called, returned sw version: {}", ret->text_representation);
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
@ -257,9 +266,13 @@ const char** PS4_SYSV_ABI getargv() {
|
|||||||
return entry_params.argv;
|
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);
|
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;
|
*Kernel::__Error() = POSIX_ESRCH;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -269,6 +282,22 @@ s32 PS4_SYSV_ABI get_authinfo(int pid, AuthInfoData* p2) {
|
|||||||
return ORBIS_OK;
|
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) {
|
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||||
service_thread = std::jthread{KernelServiceThread};
|
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_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", &g_stack_chk_guard);
|
||||||
LIB_FUNCTION("D4yla3vx4tY", "libkernel", 1, "libkernel", sceKernelError);
|
LIB_FUNCTION("D4yla3vx4tY", "libkernel", 1, "libkernel", sceKernelError);
|
||||||
|
LIB_FUNCTION("YeU23Szo3BM", "libkernel", 1, "libkernel", sceKernelGetAllowedSdkVersionOnSystem);
|
||||||
LIB_FUNCTION("Mv1zUObHvXI", "libkernel", 1, "libkernel", sceKernelGetSystemSwVersion);
|
LIB_FUNCTION("Mv1zUObHvXI", "libkernel", 1, "libkernel", sceKernelGetSystemSwVersion);
|
||||||
LIB_FUNCTION("igMefp4SAv0", "libkernel", 1, "libkernel", get_authinfo);
|
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("PfccT7qURYE", "libkernel", 1, "libkernel", kernel_ioctl);
|
||||||
LIB_FUNCTION("wW+k21cmbwQ", "libkernel", 1, "libkernel", kernel_ioctl);
|
LIB_FUNCTION("wW+k21cmbwQ", "libkernel", 1, "libkernel", kernel_ioctl);
|
||||||
LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", sceKernelGetFsSandboxRandomWord);
|
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 ORBIS(func) (Libraries::Kernel::OrbisWrapperImpl<decltype(&(func)), func>::wrap)
|
||||||
|
|
||||||
|
#define CURRENT_FIRMWARE_VERSION 0x13020011
|
||||||
|
|
||||||
s32* PS4_SYSV_ABI __Error();
|
s32* PS4_SYSV_ABI __Error();
|
||||||
|
|
||||||
struct SwVersionStruct {
|
struct SwVersionStruct {
|
||||||
@ -51,6 +53,30 @@ struct AuthInfoData {
|
|||||||
u64 ucred[8];
|
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);
|
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||||
|
|
||||||
} // namespace Libraries::Kernel
|
} // namespace Libraries::Kernel
|
||||||
|
|||||||
@ -21,8 +21,22 @@ s32 PS4_SYSV_ABI sceKernelIsNeoMode() {
|
|||||||
Common::ElfInfo::Instance().GetPSFAttributes().support_neo_mode;
|
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 PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(s32* ver) {
|
||||||
s32 version = Common::ElfInfo::Instance().RawFirmwareVer();
|
s32 version = Common::ElfInfo::Instance().CompiledSdkVer();
|
||||||
*ver = version;
|
*ver = version;
|
||||||
return (version >= 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL;
|
return (version >= 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL;
|
||||||
}
|
}
|
||||||
@ -31,6 +45,11 @@ s32 PS4_SYSV_ABI sceKernelGetCpumode() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s32 PS4_SYSV_ABI sceKernelGetCurrentCpu() {
|
||||||
|
LOG_DEBUG(Lib_Kernel, "called");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void* PS4_SYSV_ABI sceKernelGetProcParam() {
|
void* PS4_SYSV_ABI sceKernelGetProcParam() {
|
||||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||||
return linker->GetProcParam();
|
return linker->GetProcParam();
|
||||||
@ -208,7 +227,10 @@ void RegisterProcess(Core::Loader::SymbolsResolver* sym) {
|
|||||||
LIB_FUNCTION("xeu-pV8wkKs", "libkernel", 1, "libkernel", sceKernelIsInSandbox);
|
LIB_FUNCTION("xeu-pV8wkKs", "libkernel", 1, "libkernel", sceKernelIsInSandbox);
|
||||||
LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", sceKernelGetCompiledSdkVersion);
|
LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", sceKernelGetCompiledSdkVersion);
|
||||||
LIB_FUNCTION("WslcK1FQcGI", "libkernel", 1, "libkernel", sceKernelIsNeoMode);
|
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("VOx8NGmHXTs", "libkernel", 1, "libkernel", sceKernelGetCpumode);
|
||||||
|
LIB_FUNCTION("g0VTBxfJyu0", "libkernel", 1, "libkernel", sceKernelGetCurrentCpu);
|
||||||
LIB_FUNCTION("959qrazPIrg", "libkernel", 1, "libkernel", sceKernelGetProcParam);
|
LIB_FUNCTION("959qrazPIrg", "libkernel", 1, "libkernel", sceKernelGetProcParam);
|
||||||
LIB_FUNCTION("wzvqT4UqKX8", "libkernel", 1, "libkernel", sceKernelLoadStartModule);
|
LIB_FUNCTION("wzvqT4UqKX8", "libkernel", 1, "libkernel", sceKernelLoadStartModule);
|
||||||
LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", sceKernelDlsym);
|
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("Op8TBGY5KHg", "libkernel", 1, "libkernel", posix_pthread_cond_wait);
|
||||||
LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", posix_pthread_cond_broadcast);
|
LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", posix_pthread_cond_broadcast);
|
||||||
LIB_FUNCTION("2MOy+rUfuhQ", "libkernel", 1, "libkernel", posix_pthread_cond_signal);
|
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("mKoTx03HRWA", "libkernel", 1, "libkernel", posix_pthread_condattr_init);
|
||||||
LIB_FUNCTION("dJcuQVn6-Iw", "libkernel", 1, "libkernel", posix_pthread_condattr_destroy);
|
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("ltCfaGr2JGE", "libkernel", 1, "libkernel", posix_pthread_mutex_destroy);
|
||||||
LIB_FUNCTION("dQHWEsJtoE4", "libkernel", 1, "libkernel", posix_pthread_mutexattr_init);
|
LIB_FUNCTION("dQHWEsJtoE4", "libkernel", 1, "libkernel", posix_pthread_mutexattr_init);
|
||||||
LIB_FUNCTION("mDmgMOGVUqg", "libkernel", 1, "libkernel", posix_pthread_mutexattr_settype);
|
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
|
// Orbis
|
||||||
LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", ORBIS(scePthreadMutexInit));
|
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("Z4QosVuAsA0", "libkernel", 1, "libkernel", posix_pthread_once);
|
||||||
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", posix_pthread_self);
|
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", posix_pthread_self);
|
||||||
LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", posix_pthread_create);
|
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("Jb2uGFMr688", "libkernel", 1, "libkernel", posix_pthread_getaffinity_np);
|
||||||
LIB_FUNCTION("5KWrg7-ZqvE", "libkernel", 1, "libkernel", posix_pthread_setaffinity_np);
|
LIB_FUNCTION("5KWrg7-ZqvE", "libkernel", 1, "libkernel", posix_pthread_setaffinity_np);
|
||||||
LIB_FUNCTION("3eqs37G74-s", "libkernel", 1, "libkernel", posix_pthread_getthreadid_np);
|
LIB_FUNCTION("3eqs37G74-s", "libkernel", 1, "libkernel", posix_pthread_getthreadid_np);
|
||||||
|
|||||||
@ -9,6 +9,35 @@
|
|||||||
|
|
||||||
namespace Libraries::Http {
|
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() {
|
int PS4_SYSV_ABI sceHttpAbortRequest() {
|
||||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
@ -34,8 +63,9 @@ int PS4_SYSV_ABI sceHttpAddQuery() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceHttpAddRequestHeader() {
|
int PS4_SYSV_ABI sceHttpAddRequestHeader(int id, const char* name, const char* value, s32 mode) {
|
||||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
LOG_ERROR(Lib_Http, "(STUBBED) called id= {} name = {} value = {} mode = {}", id,
|
||||||
|
std::string(name), std::string(value), mode);
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,8 +114,9 @@ int PS4_SYSV_ABI sceHttpCreateConnection() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceHttpCreateConnectionWithURL() {
|
int PS4_SYSV_ABI sceHttpCreateConnectionWithURL(int tmplId, const char* url, bool enableKeepalive) {
|
||||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
LOG_ERROR(Lib_Http, "(STUBBED) called tmpid = {} url = {} enableKeepalive = {}", tmplId,
|
||||||
|
std::string(url), enableKeepalive ? 1 : 0);
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,8 +135,10 @@ int PS4_SYSV_ABI sceHttpCreateRequest2() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceHttpCreateRequestWithURL() {
|
int PS4_SYSV_ABI sceHttpCreateRequestWithURL(int connId, s32 method, const char* url,
|
||||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
u64 contentLength) {
|
||||||
|
LOG_ERROR(Lib_Http, "(STUBBED) called connId = {} method = {} url={} contentLength={}", connId,
|
||||||
|
method, url, contentLength);
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +217,7 @@ int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled() {
|
|||||||
return ORBIS_OK;
|
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");
|
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||||
return ORBIS_FAIL;
|
return ORBIS_FAIL;
|
||||||
}
|
}
|
||||||
@ -254,12 +287,42 @@ int PS4_SYSV_ABI sceHttpGetResponseContentLength() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceHttpGetStatusCode() {
|
int PS4_SYSV_ABI sceHttpGetStatusCode(int reqId, int* statusCode) {
|
||||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
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;
|
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 = {}",
|
LOG_ERROR(Lib_Http, "(DUMMY) called libnetMemId = {} libsslCtxId = {} poolSize = {}",
|
||||||
libnetMemId, libsslCtxId, poolSize);
|
libnetMemId, libsslCtxId, poolSize);
|
||||||
// return a value >1
|
// return a value >1
|
||||||
@ -267,14 +330,104 @@ int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, std::size_t poolS
|
|||||||
return ++id;
|
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");
|
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceHttpParseStatusLine() {
|
int PS4_SYSV_ABI sceHttpParseStatusLine(const char* statusLine, u64 lineLen, int32_t* httpMajorVer,
|
||||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
int32_t* httpMinorVer, int32_t* responseCode,
|
||||||
return ORBIS_OK;
|
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() {
|
int PS4_SYSV_ABI sceHttpReadData() {
|
||||||
@ -317,8 +470,8 @@ int PS4_SYSV_ABI sceHttpsEnableOptionPrivate() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceHttpSendRequest() {
|
int PS4_SYSV_ABI sceHttpSendRequest(int reqId, const void* postData, u64 size) {
|
||||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
LOG_ERROR(Lib_Http, "(STUBBED) called reqId = {} size = {}", reqId, size);
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -548,7 +701,8 @@ int PS4_SYSV_ABI sceHttpUnsetEpoll() {
|
|||||||
return ORBIS_OK;
|
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");
|
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
@ -563,13 +717,97 @@ int PS4_SYSV_ABI sceHttpUriEscape() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceHttpUriMerge() {
|
int PS4_SYSV_ABI sceHttpUriMerge(char* mergedUrl, char* url, char* relativeUri, u64* require,
|
||||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
u64 prepare, u32 option) {
|
||||||
return ORBIS_OK;
|
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,
|
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));
|
LOG_INFO(Lib_Http, "srcUri = {}", std::string(srcUri));
|
||||||
if (!srcUri) {
|
if (!srcUri) {
|
||||||
LOG_ERROR(Lib_Http, "invalid url");
|
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
|
// Track the total required buffer size
|
||||||
size_t requiredSize = 0;
|
u64 requiredSize = 0;
|
||||||
|
|
||||||
// Parse the scheme (e.g., "http:", "https:", "file:")
|
// Parse the scheme (e.g., "http:", "https:", "file:")
|
||||||
size_t schemeLength = 0;
|
u64 schemeLength = 0;
|
||||||
while (srcUri[schemeLength] && srcUri[schemeLength] != ':') {
|
while (srcUri[schemeLength] && srcUri[schemeLength] != ':') {
|
||||||
if (!isalnum(srcUri[schemeLength])) {
|
if (!isalnum(srcUri[schemeLength])) {
|
||||||
LOG_ERROR(Lib_Http, "invalid url");
|
LOG_ERROR(Lib_Http, "invalid url");
|
||||||
@ -611,7 +849,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
|||||||
requiredSize += schemeLength + 1;
|
requiredSize += schemeLength + 1;
|
||||||
|
|
||||||
// Move past the scheme and ':' character
|
// Move past the scheme and ':' character
|
||||||
size_t offset = schemeLength + 1;
|
u64 offset = schemeLength + 1;
|
||||||
|
|
||||||
// Check if "//" appears after the scheme
|
// Check if "//" appears after the scheme
|
||||||
if (strncmp(srcUri + offset, "//", 2) == 0) {
|
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)
|
// Parse the path (everything after the slashes)
|
||||||
char* pathStart = (char*)srcUri + offset;
|
char* pathStart = (char*)srcUri + offset;
|
||||||
size_t pathLength = 0;
|
u64 pathLength = 0;
|
||||||
while (pathStart[pathLength] && pathStart[pathLength] != '?' &&
|
while (pathStart[pathLength] && pathStart[pathLength] != '?' &&
|
||||||
pathStart[pathLength] != '#') {
|
pathStart[pathLength] != '#') {
|
||||||
pathLength++;
|
pathLength++;
|
||||||
@ -689,7 +927,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
|||||||
hostStart++;
|
hostStart++;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t hostLength = 0;
|
u64 hostLength = 0;
|
||||||
while (hostStart[hostLength] && hostStart[hostLength] != '/' &&
|
while (hostStart[hostLength] && hostStart[hostLength] != '/' &&
|
||||||
hostStart[hostLength] != '?' && hostStart[hostLength] != ':') {
|
hostStart[hostLength] != '?' && hostStart[hostLength] != ':') {
|
||||||
hostLength++;
|
hostLength++;
|
||||||
@ -714,7 +952,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
|||||||
// Parse the port (if present)
|
// Parse the port (if present)
|
||||||
if (hostStart[hostLength] == ':') {
|
if (hostStart[hostLength] == ':') {
|
||||||
char* portStart = hostStart + hostLength + 1;
|
char* portStart = hostStart + hostLength + 1;
|
||||||
size_t portLength = 0;
|
u64 portLength = 0;
|
||||||
while (portStart[portLength] && isdigit(portStart[portLength])) {
|
while (portStart[portLength] && isdigit(portStart[portLength])) {
|
||||||
portLength++;
|
portLength++;
|
||||||
}
|
}
|
||||||
@ -754,7 +992,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
|||||||
// Parse the path (if present)
|
// Parse the path (if present)
|
||||||
if (srcUri[offset] == '/') {
|
if (srcUri[offset] == '/') {
|
||||||
char* pathStart = (char*)srcUri + offset;
|
char* pathStart = (char*)srcUri + offset;
|
||||||
size_t pathLength = 0;
|
u64 pathLength = 0;
|
||||||
while (pathStart[pathLength] && pathStart[pathLength] != '?' &&
|
while (pathStart[pathLength] && pathStart[pathLength] != '?' &&
|
||||||
pathStart[pathLength] != '#') {
|
pathStart[pathLength] != '#') {
|
||||||
pathLength++;
|
pathLength++;
|
||||||
@ -780,7 +1018,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
|||||||
// Parse the query (if present)
|
// Parse the query (if present)
|
||||||
if (srcUri[offset] == '?') {
|
if (srcUri[offset] == '?') {
|
||||||
char* queryStart = (char*)srcUri + offset + 1;
|
char* queryStart = (char*)srcUri + offset + 1;
|
||||||
size_t queryLength = 0;
|
u64 queryLength = 0;
|
||||||
while (queryStart[queryLength] && queryStart[queryLength] != '#') {
|
while (queryStart[queryLength] && queryStart[queryLength] != '#') {
|
||||||
queryLength++;
|
queryLength++;
|
||||||
}
|
}
|
||||||
@ -805,7 +1043,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
|||||||
// Parse the fragment (if present)
|
// Parse the fragment (if present)
|
||||||
if (srcUri[offset] == '#') {
|
if (srcUri[offset] == '#') {
|
||||||
char* fragmentStart = (char*)srcUri + offset + 1;
|
char* fragmentStart = (char*)srcUri + offset + 1;
|
||||||
size_t fragmentLength = 0;
|
u64 fragmentLength = 0;
|
||||||
while (fragmentStart[fragmentLength]) {
|
while (fragmentStart[fragmentLength]) {
|
||||||
fragmentLength++;
|
fragmentLength++;
|
||||||
}
|
}
|
||||||
@ -833,12 +1071,12 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
|||||||
return ORBIS_OK;
|
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");
|
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||||
return ORBIS_OK;
|
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");
|
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "core/libraries/network/ssl.h"
|
#include "core/libraries/network/ssl.h"
|
||||||
|
|
||||||
@ -25,6 +26,12 @@ struct OrbisHttpUriElement {
|
|||||||
u8 reserved[10];
|
u8 reserved[10];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct HttpRequestInternal {
|
||||||
|
int state; // +0x20
|
||||||
|
int errorCode; // +0x28
|
||||||
|
int httpStatusCode; // +0x20C
|
||||||
|
std::mutex m_mutex;
|
||||||
|
};
|
||||||
using OrbisHttpsCaList = Libraries::Ssl::OrbisSslCaList;
|
using OrbisHttpsCaList = Libraries::Ssl::OrbisSslCaList;
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceHttpAbortRequest();
|
int PS4_SYSV_ABI sceHttpAbortRequest();
|
||||||
@ -32,7 +39,7 @@ int PS4_SYSV_ABI sceHttpAbortRequestForce();
|
|||||||
int PS4_SYSV_ABI sceHttpAbortWaitRequest();
|
int PS4_SYSV_ABI sceHttpAbortWaitRequest();
|
||||||
int PS4_SYSV_ABI sceHttpAddCookie();
|
int PS4_SYSV_ABI sceHttpAddCookie();
|
||||||
int PS4_SYSV_ABI sceHttpAddQuery();
|
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 sceHttpAddRequestHeaderRaw();
|
||||||
int PS4_SYSV_ABI sceHttpAuthCacheExport();
|
int PS4_SYSV_ABI sceHttpAuthCacheExport();
|
||||||
int PS4_SYSV_ABI sceHttpAuthCacheFlush();
|
int PS4_SYSV_ABI sceHttpAuthCacheFlush();
|
||||||
@ -42,11 +49,12 @@ int PS4_SYSV_ABI sceHttpCookieExport();
|
|||||||
int PS4_SYSV_ABI sceHttpCookieFlush();
|
int PS4_SYSV_ABI sceHttpCookieFlush();
|
||||||
int PS4_SYSV_ABI sceHttpCookieImport();
|
int PS4_SYSV_ABI sceHttpCookieImport();
|
||||||
int PS4_SYSV_ABI sceHttpCreateConnection();
|
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 sceHttpCreateEpoll();
|
||||||
int PS4_SYSV_ABI sceHttpCreateRequest();
|
int PS4_SYSV_ABI sceHttpCreateRequest();
|
||||||
int PS4_SYSV_ABI sceHttpCreateRequest2();
|
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 sceHttpCreateRequestWithURL2();
|
||||||
int PS4_SYSV_ABI sceHttpCreateTemplate();
|
int PS4_SYSV_ABI sceHttpCreateTemplate();
|
||||||
int PS4_SYSV_ABI sceHttpDbgEnableProfile();
|
int PS4_SYSV_ABI sceHttpDbgEnableProfile();
|
||||||
@ -62,7 +70,7 @@ int PS4_SYSV_ABI sceHttpDeleteRequest();
|
|||||||
int PS4_SYSV_ABI sceHttpDeleteTemplate();
|
int PS4_SYSV_ABI sceHttpDeleteTemplate();
|
||||||
int PS4_SYSV_ABI sceHttpDestroyEpoll();
|
int PS4_SYSV_ABI sceHttpDestroyEpoll();
|
||||||
int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled();
|
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 sceHttpGetAuthEnabled();
|
||||||
int PS4_SYSV_ABI sceHttpGetAutoRedirect();
|
int PS4_SYSV_ABI sceHttpGetAutoRedirect();
|
||||||
int PS4_SYSV_ABI sceHttpGetConnectionStat();
|
int PS4_SYSV_ABI sceHttpGetConnectionStat();
|
||||||
@ -76,10 +84,13 @@ int PS4_SYSV_ABI sceHttpGetMemoryPoolStats();
|
|||||||
int PS4_SYSV_ABI sceHttpGetNonblock();
|
int PS4_SYSV_ABI sceHttpGetNonblock();
|
||||||
int PS4_SYSV_ABI sceHttpGetRegisteredCtxIds();
|
int PS4_SYSV_ABI sceHttpGetRegisteredCtxIds();
|
||||||
int PS4_SYSV_ABI sceHttpGetResponseContentLength();
|
int PS4_SYSV_ABI sceHttpGetResponseContentLength();
|
||||||
int PS4_SYSV_ABI sceHttpGetStatusCode();
|
int PS4_SYSV_ABI sceHttpGetStatusCode(int reqId, int* statusCode);
|
||||||
int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, std::size_t poolSize);
|
int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, u64 poolSize);
|
||||||
int PS4_SYSV_ABI sceHttpParseResponseHeader();
|
int PS4_SYSV_ABI sceHttpParseResponseHeader(const char* header, u64 headerLen, const char* fieldStr,
|
||||||
int PS4_SYSV_ABI sceHttpParseStatusLine();
|
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 sceHttpReadData();
|
||||||
int PS4_SYSV_ABI sceHttpRedirectCacheFlush();
|
int PS4_SYSV_ABI sceHttpRedirectCacheFlush();
|
||||||
int PS4_SYSV_ABI sceHttpRemoveRequestHeader();
|
int PS4_SYSV_ABI sceHttpRemoveRequestHeader();
|
||||||
@ -88,7 +99,7 @@ int PS4_SYSV_ABI sceHttpsDisableOption();
|
|||||||
int PS4_SYSV_ABI sceHttpsDisableOptionPrivate();
|
int PS4_SYSV_ABI sceHttpsDisableOptionPrivate();
|
||||||
int PS4_SYSV_ABI sceHttpsEnableOption();
|
int PS4_SYSV_ABI sceHttpsEnableOption();
|
||||||
int PS4_SYSV_ABI sceHttpsEnableOptionPrivate();
|
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 sceHttpSetAcceptEncodingGZIPEnabled();
|
||||||
int PS4_SYSV_ABI sceHttpSetAuthEnabled();
|
int PS4_SYSV_ABI sceHttpSetAuthEnabled();
|
||||||
int PS4_SYSV_ABI sceHttpSetAuthInfoCallback();
|
int PS4_SYSV_ABI sceHttpSetAuthInfoCallback();
|
||||||
@ -134,14 +145,16 @@ int PS4_SYSV_ABI sceHttpTerm();
|
|||||||
int PS4_SYSV_ABI sceHttpTryGetNonblock();
|
int PS4_SYSV_ABI sceHttpTryGetNonblock();
|
||||||
int PS4_SYSV_ABI sceHttpTrySetNonblock();
|
int PS4_SYSV_ABI sceHttpTrySetNonblock();
|
||||||
int PS4_SYSV_ABI sceHttpUnsetEpoll();
|
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 sceHttpUriCopy();
|
||||||
int PS4_SYSV_ABI sceHttpUriEscape();
|
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,
|
int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool,
|
||||||
size_t* require, size_t prepare);
|
u64* require, u64 prepare);
|
||||||
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);
|
||||||
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);
|
||||||
int PS4_SYSV_ABI sceHttpWaitRequest();
|
int PS4_SYSV_ABI sceHttpWaitRequest();
|
||||||
|
|
||||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
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) {
|
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);
|
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() {
|
int PS4_SYSV_ABI sceNetPoolDestroy() {
|
||||||
|
|||||||
@ -5,7 +5,6 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <SDL3/SDL_audio.h>
|
|
||||||
#include <cmrc/cmrc.hpp>
|
#include <cmrc/cmrc.hpp>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
@ -92,59 +91,45 @@ TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::strin
|
|||||||
|
|
||||||
AddLayer(this);
|
AddLayer(this);
|
||||||
|
|
||||||
bool customsoundplayed = false;
|
MIX_Init();
|
||||||
#ifdef ENABLE_QT_GUI
|
mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL);
|
||||||
QString musicPathWav = QString::fromStdString(CustomTrophy_Dir.string() + "/trophy.wav");
|
if (!mixer) {
|
||||||
QString musicPathMp3 = QString::fromStdString(CustomTrophy_Dir.string() + "/trophy.mp3");
|
LOG_ERROR(Lib_NpTrophy, "Could not initialize SDL Mixer, {}", SDL_GetError());
|
||||||
if (fs::exists(musicPathWav.toStdString())) {
|
return;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
#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");
|
auto soundFile = resource.open("src/images/trophy.wav");
|
||||||
std::vector<u8> soundData = std::vector<u8>(soundFile.begin(), soundFile.end());
|
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;
|
if (!audio) {
|
||||||
Uint8* audioBuf;
|
LOG_ERROR(Lib_NpTrophy, "Could not loud audio file, {}", SDL_GetError());
|
||||||
Uint32 audioLen;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!SDL_LoadWAV_IO(SDL_IOFromMem(soundData.data(), soundData.size()), true, &spec,
|
if (!MIX_PlayAudio(mixer, audio)) {
|
||||||
&audioBuf, &audioLen)) {
|
LOG_ERROR(Lib_NpTrophy, "Could not play audio file, {}", SDL_GetError());
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TrophyUI::~TrophyUI() {
|
TrophyUI::~TrophyUI() {
|
||||||
|
MIX_DestroyAudio(audio);
|
||||||
|
MIX_DestroyMixer(mixer);
|
||||||
|
MIX_Quit();
|
||||||
|
|
||||||
Finish();
|
Finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
#include <SDL3_mixer/SDL_mixer.h>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
#include "common/fixed_value.h"
|
#include "common/fixed_value.h"
|
||||||
@ -30,6 +31,9 @@ private:
|
|||||||
std::string_view trophy_type;
|
std::string_view trophy_type;
|
||||||
ImGui::RefCountedTexture trophy_icon;
|
ImGui::RefCountedTexture trophy_icon;
|
||||||
ImGui::RefCountedTexture trophy_type_icon;
|
ImGui::RefCountedTexture trophy_type_icon;
|
||||||
|
|
||||||
|
MIX_Mixer* mixer;
|
||||||
|
MIX_Audio* audio;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TrophyInfo {
|
struct TrophyInfo {
|
||||||
|
|||||||
@ -180,7 +180,7 @@ void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_cor
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ignore_corrupt && !read_only) {
|
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();
|
f.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -59,7 +59,7 @@ void PersistMemory(u32 slot_id, bool lock) {
|
|||||||
while (n++ < 10) {
|
while (n++ < 10) {
|
||||||
try {
|
try {
|
||||||
IOFile f;
|
IOFile f;
|
||||||
int r = f.Open(memoryPath, Common::FS::FileAccessMode::Write);
|
int r = f.Open(memoryPath, Common::FS::FileAccessMode::Create);
|
||||||
if (f.IsOpen()) {
|
if (f.IsOpen()) {
|
||||||
f.WriteRaw<u8>(data.memory_cache.data(), data.memory_cache.size());
|
f.WriteRaw<u8>(data.memory_cache.data(), data.memory_cache.size());
|
||||||
f.Close();
|
f.Close();
|
||||||
@ -148,7 +148,7 @@ void SetIcon(u32 slot_id, void* buf, size_t buf_size) {
|
|||||||
fs::copy_file(src_icon, icon_path);
|
fs::copy_file(src_icon, icon_path);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
IOFile file(icon_path, Common::FS::FileAccessMode::Write);
|
IOFile file(icon_path, Common::FS::FileAccessMode::Create);
|
||||||
file.WriteRaw<u8>(buf, buf_size);
|
file.WriteRaw<u8>(buf, buf_size);
|
||||||
file.Close();
|
file.Close();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1389,7 +1389,7 @@ Error PS4_SYSV_ABI sceSaveDataSaveIcon(const OrbisSaveDataMountPoint* mountPoint
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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));
|
file.WriteRaw<u8>(icon->buf, std::min(icon->bufSize, icon->dataSize));
|
||||||
} catch (const fs::filesystem_error& e) {
|
} catch (const fs::filesystem_error& e) {
|
||||||
LOG_ERROR(Lib_SaveData, "Failed to load icon: {}", e.what());
|
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 endpoint_descs = FillEndpointDescriptorPair();
|
||||||
const auto interface_desc = FillInterfaceDescriptor(endpoint_descs);
|
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->altsetting = interface_desc;
|
||||||
interface->num_altsetting = 1;
|
interface->num_altsetting = 1;
|
||||||
|
|
||||||
@ -366,7 +366,7 @@ public:
|
|||||||
const auto desc = FillDeviceDescriptor();
|
const auto desc = FillDeviceDescriptor();
|
||||||
ASSERT(desc);
|
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->bus_number = 0;
|
||||||
fake->port_number = 0;
|
fake->port_number = 0;
|
||||||
fake->device_address = 0;
|
fake->device_address = 0;
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
#include "emulated/dimensions.h"
|
||||||
|
#include "emulated/infinity.h"
|
||||||
#include "emulated/skylander.h"
|
#include "emulated/skylander.h"
|
||||||
#include "usb_backend.h"
|
#include "usb_backend.h"
|
||||||
|
|
||||||
@ -33,10 +35,9 @@ using SceUsbdTransfer = libusb_transfer;
|
|||||||
using SceUsbdControlSetup = libusb_control_setup;
|
using SceUsbdControlSetup = libusb_control_setup;
|
||||||
using SceUsbdTransferCallback = void PS4_SYSV_ABI (*)(SceUsbdTransfer* transfer);
|
using SceUsbdTransferCallback = void PS4_SYSV_ABI (*)(SceUsbdTransfer* transfer);
|
||||||
|
|
||||||
// TODO: implement emulated devices
|
|
||||||
using SkylandersPortalBackend = SkylanderBackend;
|
using SkylandersPortalBackend = SkylanderBackend;
|
||||||
using InfinityBaseBackend = UsbRealBackend;
|
using InfinityBaseBackend = InfinityBackend;
|
||||||
using DimensionsToypadBackend = UsbRealBackend;
|
using DimensionsToypadBackend = DimensionsBackend;
|
||||||
|
|
||||||
enum class SceUsbdSpeed : u32 {
|
enum class SceUsbdSpeed : u32 {
|
||||||
UNKNOWN = 0,
|
UNKNOWN = 0,
|
||||||
|
|||||||
@ -502,19 +502,19 @@ bool Elf::IsSharedLib() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Elf::ElfHeaderDebugDump(const std::filesystem::path& file_name) {
|
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};
|
Common::FS::FileType::TextFile};
|
||||||
f.WriteString(ElfHeaderStr());
|
f.WriteString(ElfHeaderStr());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Elf::SelfHeaderDebugDump(const std::filesystem::path& file_name) {
|
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};
|
Common::FS::FileType::TextFile};
|
||||||
f.WriteString(SElfHeaderStr());
|
f.WriteString(SElfHeaderStr());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Elf::SelfSegHeaderDebugDump(const std::filesystem::path& file_name) {
|
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};
|
Common::FS::FileType::TextFile};
|
||||||
for (u16 i = 0; i < m_self.segment_count; i++) {
|
for (u16 i = 0; i < m_self.segment_count; i++) {
|
||||||
f.WriteString(SELFSegHeader(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) {
|
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};
|
Common::FS::FileType::TextFile};
|
||||||
if (m_elf_header.e_phentsize > 0) {
|
if (m_elf_header.e_phentsize > 0) {
|
||||||
for (u16 i = 0; i < m_elf_header.e_phnum; i++) {
|
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) {
|
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};
|
Common::FS::FileType::TextFile};
|
||||||
for (const auto& symbol : m_symbols) {
|
for (const auto& symbol : m_symbols) {
|
||||||
const auto ids = Common::SplitString(symbol.name, '#');
|
const auto ids = Common::SplitString(symbol.name, '#');
|
||||||
|
|||||||
@ -31,6 +31,8 @@
|
|||||||
#include "core/file_format/trp.h"
|
#include "core/file_format/trp.h"
|
||||||
#include "core/file_sys/fs.h"
|
#include "core/file_sys/fs.h"
|
||||||
#include "core/libraries/disc_map/disc_map.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/libc_internal/libc_internal.h"
|
||||||
#include "core/libraries/libs.h"
|
#include "core/libraries/libs.h"
|
||||||
#include "core/libraries/ngs2/ngs2.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 id;
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string app_version;
|
std::string app_version;
|
||||||
|
u32 sdk_version;
|
||||||
u32 fw_version;
|
u32 fw_version;
|
||||||
Common::PSFAttributes psf_attributes{};
|
Common::PSFAttributes psf_attributes{};
|
||||||
if (param_sfo_exists) {
|
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")) {
|
if (const auto raw_attributes = param_sfo->GetInteger("ATTRIBUTE")) {
|
||||||
psf_attributes.raw = *raw_attributes;
|
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"),
|
Config::load(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (id + ".toml"),
|
||||||
true);
|
true);
|
||||||
|
|
||||||
@ -194,6 +237,7 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
|||||||
if (param_sfo_exists) {
|
if (param_sfo_exists) {
|
||||||
LOG_INFO(Loader, "Game id: {} Title: {}", id, title);
|
LOG_INFO(Loader, "Game id: {} Title: {}", id, title);
|
||||||
LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version);
|
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 Supported: {}", (bool)psf_attributes.support_ps_vr.Value());
|
||||||
LOG_INFO(Loader, "PSVR Required: {}", (bool)psf_attributes.require_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 game_title = fmt::format("{} - {} <{}>", id, title, app_version);
|
||||||
std::string window_title = "";
|
std::string window_title = "";
|
||||||
std::string remote_url(Common::g_scm_remote_url);
|
std::string remote_url(Common::g_scm_remote_url);
|
||||||
@ -473,8 +501,8 @@ void Emulator::LoadSystemModules(const std::string& game_serial) {
|
|||||||
{"libSceJson2.sprx", nullptr},
|
{"libSceJson2.sprx", nullptr},
|
||||||
{"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterLib},
|
{"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterLib},
|
||||||
{"libSceCesCs.sprx", nullptr},
|
{"libSceCesCs.sprx", nullptr},
|
||||||
{"libSceFont.sprx", nullptr},
|
{"libSceFont.sprx", &Libraries::Font::RegisterlibSceFont},
|
||||||
{"libSceFontFt.sprx", nullptr},
|
{"libSceFontFt.sprx", &Libraries::FontFt::RegisterlibSceFontFt},
|
||||||
{"libSceFreeTypeOt.sprx", nullptr}});
|
{"libSceFreeTypeOt.sprx", nullptr}});
|
||||||
|
|
||||||
std::vector<std::filesystem::path> found_modules;
|
std::vector<std::filesystem::path> found_modules;
|
||||||
|
|||||||
@ -250,7 +250,7 @@ void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool MustKeepDrawing() {
|
bool MustKeepDrawing() {
|
||||||
return layers.size() > 1 || DebugState.IsShowingDebugMenuBar();
|
return layers.size() > 1 || change_layers.size() > 1 || DebugState.IsShowingDebugMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|||||||
@ -559,7 +559,7 @@ void Translator::EmitFetch(const GcnInst& inst) {
|
|||||||
std::filesystem::create_directories(dump_dir);
|
std::filesystem::create_directories(dump_dir);
|
||||||
}
|
}
|
||||||
const auto filename = fmt::format("vs_{:#018x}.fetch.bin", info.pgm_hash);
|
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);
|
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);
|
std::filesystem::create_directories(dump_dir);
|
||||||
}
|
}
|
||||||
const auto filename = fmt::format("{}_{:#018x}.srtprogram.txt", info.stage, info.pgm_hash);
|
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 address = reinterpret_cast<u64>(code);
|
||||||
u64 code_end = address + codesize;
|
u64 code_end = address + codesize;
|
||||||
|
|||||||
@ -190,7 +190,7 @@ void CollectShaderInfoPass(IR::Program& program, const Profile& profile) {
|
|||||||
});
|
});
|
||||||
info.buffers.push_back({
|
info.buffers.push_back({
|
||||||
.used_types = IR::Type::U32,
|
.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,
|
.buffer_type = BufferType::FaultBuffer,
|
||||||
.is_written = true,
|
.is_written = true,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -28,7 +28,7 @@ void DumpProgram(const Program& program, const Info& info, const std::string& ty
|
|||||||
}
|
}
|
||||||
const auto ir_filename =
|
const auto ir_filename =
|
||||||
fmt::format("{}_{:#018x}.{}irprogram.txt", info.stage, info.pgm_hash, type);
|
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};
|
size_t index{0};
|
||||||
std::map<const IR::Inst*, size_t> inst_to_index;
|
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_filename = fmt::format("{}_{:#018x}.{}asl.txt", info.stage, info.pgm_hash, type);
|
||||||
const auto asl_file =
|
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) {
|
for (const auto& node : program.syntax_list) {
|
||||||
std::string s = IR::DumpASLNode(node, block_to_index, inst_to_index) + '\n';
|
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
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <mutex>
|
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "common/types.h"
|
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "video_core/amdgpu/liverpool.h"
|
#include "video_core/amdgpu/liverpool.h"
|
||||||
#include "video_core/buffer_cache/buffer_cache.h"
|
#include "video_core/buffer_cache/buffer_cache.h"
|
||||||
#include "video_core/buffer_cache/memory_tracker.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_graphics_pipeline.h"
|
||||||
#include "video_core/renderer_vulkan/vk_instance.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_scheduler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
|
||||||
#include "video_core/texture_cache/texture_cache.h"
|
#include "video_core/texture_cache/texture_cache.h"
|
||||||
|
|
||||||
namespace VideoCore {
|
namespace VideoCore {
|
||||||
|
|
||||||
static constexpr size_t DataShareBufferSize = 64_KB;
|
static constexpr size_t DataShareBufferSize = 64_KB;
|
||||||
static constexpr size_t StagingBufferSize = 512_MB;
|
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 UboStreamBufferSize = 64_MB;
|
||||||
static constexpr size_t DownloadBufferSize = 128_MB;
|
|
||||||
static constexpr size_t DeviceBufferSize = 128_MB;
|
static constexpr size_t DeviceBufferSize = 128_MB;
|
||||||
static constexpr size_t MaxPageFaults = 1024;
|
|
||||||
|
|
||||||
BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_,
|
BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_,
|
||||||
AmdGpu::Liverpool* liverpool_, TextureCache& texture_cache_,
|
AmdGpu::Liverpool* liverpool_, TextureCache& texture_cache_,
|
||||||
PageManager& tracker)
|
PageManager& tracker)
|
||||||
: instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_},
|
: instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_},
|
||||||
memory{Core::Memory::Instance()}, texture_cache{texture_cache_},
|
memory{Core::Memory::Instance()}, texture_cache{texture_cache_},
|
||||||
|
fault_manager{instance, scheduler, *this, CACHING_PAGEBITS, CACHING_NUMPAGES},
|
||||||
staging_buffer{instance, scheduler, MemoryUsage::Upload, StagingBufferSize},
|
staging_buffer{instance, scheduler, MemoryUsage::Upload, StagingBufferSize},
|
||||||
stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize},
|
stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize},
|
||||||
download_buffer{instance, scheduler, MemoryUsage::Download, DownloadBufferSize},
|
download_buffer{instance, scheduler, MemoryUsage::Download, DownloadBufferSize},
|
||||||
device_buffer{instance, scheduler, MemoryUsage::DeviceLocal, DeviceBufferSize},
|
device_buffer{instance, scheduler, MemoryUsage::DeviceLocal, DeviceBufferSize},
|
||||||
gds_buffer{instance, scheduler, MemoryUsage::Stream, 0, AllFlags, DataShareBufferSize},
|
gds_buffer{instance, scheduler, MemoryUsage::Stream, 0, AllFlags, DataShareBufferSize},
|
||||||
bda_pagetable_buffer{instance, scheduler, MemoryUsage::DeviceLocal,
|
bda_pagetable_buffer{instance, scheduler, MemoryUsage::DeviceLocal,
|
||||||
0, AllFlags, BDA_PAGETABLE_SIZE},
|
0, AllFlags, BDA_PAGETABLE_SIZE} {
|
||||||
fault_buffer(instance, scheduler, MemoryUsage::DeviceLocal, 0, AllFlags, FAULT_BUFFER_SIZE) {
|
|
||||||
Vulkan::SetObjectName(instance.GetDevice(), gds_buffer.Handle(), "GDS Buffer");
|
Vulkan::SetObjectName(instance.GetDevice(), gds_buffer.Handle(), "GDS Buffer");
|
||||||
Vulkan::SetObjectName(instance.GetDevice(), bda_pagetable_buffer.Handle(),
|
Vulkan::SetObjectName(instance.GetDevice(), bda_pagetable_buffer.Handle(),
|
||||||
"BDA Page Table Buffer");
|
"BDA Page Table Buffer");
|
||||||
Vulkan::SetObjectName(instance.GetDevice(), fault_buffer.Handle(), "Fault Buffer");
|
|
||||||
|
|
||||||
memory_tracker = std::make_unique<MemoryTracker>(tracker);
|
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;
|
const vk::Buffer& null_buffer = slot_buffers[null_id].buffer;
|
||||||
Vulkan::SetObjectName(instance.GetDevice(), null_buffer, "Null 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
|
// Set up garbage collection parameters
|
||||||
if (!instance.CanReportMemoryUsage()) {
|
if (!instance.CanReportMemoryUsage()) {
|
||||||
trigger_gc_memory = DEFAULT_TRIGGER_GC_MEMORY;
|
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);
|
wanted_size = static_cast<u32>(device_addr_end - device_addr);
|
||||||
const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size);
|
const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size);
|
||||||
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
|
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
|
||||||
const BufferId new_buffer_id = [&] {
|
const BufferId new_buffer_id =
|
||||||
std::scoped_lock lk{slot_buffers_mutex};
|
slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin,
|
||||||
return slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin,
|
AllFlags | vk::BufferUsageFlagBits::eShaderDeviceAddress, size);
|
||||||
AllFlags | vk::BufferUsageFlagBits::eShaderDeviceAddress, size);
|
|
||||||
}();
|
|
||||||
auto& new_buffer = slot_buffers[new_buffer_id];
|
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) {
|
for (const BufferId overlap_id : overlap.ids) {
|
||||||
JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap);
|
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() {
|
void BufferCache::ProcessFaultBuffer() {
|
||||||
// Run fault processing shader
|
fault_manager.ProcessFaultBuffer();
|
||||||
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));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCache::Register(BufferId buffer_id) {
|
void BufferCache::Register(BufferId buffer_id) {
|
||||||
@ -889,7 +685,7 @@ bool BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size,
|
|||||||
});
|
});
|
||||||
TouchBuffer(buffer);
|
TouchBuffer(buffer);
|
||||||
}
|
}
|
||||||
if (is_texel_buffer) {
|
if (is_texel_buffer && !is_written) {
|
||||||
return SynchronizeBufferFromImage(buffer, device_addr, size);
|
return SynchronizeBufferFromImage(buffer, device_addr, size);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -972,10 +768,7 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BufferCache::SynchronizeBuffersInRange(VAddr device_addr, u64 size) {
|
void BufferCache::SynchronizeBuffersInRange(VAddr device_addr, u64 size) {
|
||||||
if (device_addr == 0) {
|
const VAddr device_addr_end = device_addr + size;
|
||||||
return;
|
|
||||||
}
|
|
||||||
VAddr device_addr_end = device_addr + size;
|
|
||||||
ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
|
ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) {
|
||||||
RENDERER_TRACE;
|
RENDERER_TRACE;
|
||||||
VAddr start = std::max(buffer.CpuAddr(), device_addr);
|
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,
|
void BufferCache::InlineDataBuffer(Buffer& buffer, VAddr address, const void* value,
|
||||||
u32 num_bytes) {
|
u32 num_bytes) {
|
||||||
scheduler.EndRendering();
|
scheduler.EndRendering();
|
||||||
|
|||||||
@ -3,12 +3,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <shared_mutex>
|
|
||||||
#include <boost/container/small_vector.hpp>
|
#include <boost/container/small_vector.hpp>
|
||||||
#include "common/lru_cache.h"
|
#include "common/lru_cache.h"
|
||||||
#include "common/slot_vector.h"
|
#include "common/slot_vector.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "video_core/buffer_cache/buffer.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/buffer_cache/range_set.h"
|
||||||
#include "video_core/multi_level_page_table.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 CACHING_PAGESIZE = u64{1} << CACHING_PAGEBITS;
|
||||||
static constexpr u64 DEVICE_PAGESIZE = 16_KB;
|
static constexpr u64 DEVICE_PAGESIZE = 16_KB;
|
||||||
static constexpr u64 CACHING_NUMPAGES = u64{1} << (40 - CACHING_PAGEBITS);
|
static constexpr u64 CACHING_NUMPAGES = u64{1} << (40 - CACHING_PAGEBITS);
|
||||||
|
|
||||||
static constexpr u64 BDA_PAGETABLE_SIZE = CACHING_NUMPAGES * sizeof(vk::DeviceAddress);
|
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
|
// Default values for garbage collection
|
||||||
static constexpr s64 DEFAULT_TRIGGER_GC_MEMORY = 1_GB;
|
static constexpr s64 DEFAULT_TRIGGER_GC_MEMORY = 1_GB;
|
||||||
@ -68,12 +66,6 @@ public:
|
|||||||
bool has_stream_leap = false;
|
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:
|
public:
|
||||||
explicit BufferCache(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler,
|
explicit BufferCache(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler,
|
||||||
AmdGpu::Liverpool* liverpool, TextureCache& texture_cache,
|
AmdGpu::Liverpool* liverpool, TextureCache& texture_cache,
|
||||||
@ -92,7 +84,7 @@ public:
|
|||||||
|
|
||||||
/// Retrieves the fault buffer.
|
/// Retrieves the fault buffer.
|
||||||
[[nodiscard]] Buffer* GetFaultBuffer() noexcept {
|
[[nodiscard]] Buffer* GetFaultBuffer() noexcept {
|
||||||
return &fault_buffer;
|
return fault_manager.GetFaultBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the buffer with the specified id.
|
/// Retrieves the buffer with the specified id.
|
||||||
@ -160,9 +152,6 @@ public:
|
|||||||
/// Synchronizes all buffers neede for DMA.
|
/// Synchronizes all buffers neede for DMA.
|
||||||
void SynchronizeDmaBuffers();
|
void SynchronizeDmaBuffers();
|
||||||
|
|
||||||
/// Record memory barrier. Used for buffers when accessed via BDA.
|
|
||||||
void MemoryBarrier();
|
|
||||||
|
|
||||||
/// Runs the garbage collector.
|
/// Runs the garbage collector.
|
||||||
void RunGarbageCollector();
|
void RunGarbageCollector();
|
||||||
|
|
||||||
@ -217,6 +206,7 @@ private:
|
|||||||
AmdGpu::Liverpool* liverpool;
|
AmdGpu::Liverpool* liverpool;
|
||||||
Core::MemoryManager* memory;
|
Core::MemoryManager* memory;
|
||||||
TextureCache& texture_cache;
|
TextureCache& texture_cache;
|
||||||
|
FaultManager fault_manager;
|
||||||
std::unique_ptr<MemoryTracker> memory_tracker;
|
std::unique_ptr<MemoryTracker> memory_tracker;
|
||||||
StreamBuffer staging_buffer;
|
StreamBuffer staging_buffer;
|
||||||
StreamBuffer stream_buffer;
|
StreamBuffer stream_buffer;
|
||||||
@ -224,8 +214,6 @@ private:
|
|||||||
StreamBuffer device_buffer;
|
StreamBuffer device_buffer;
|
||||||
Buffer gds_buffer;
|
Buffer gds_buffer;
|
||||||
Buffer bda_pagetable_buffer;
|
Buffer bda_pagetable_buffer;
|
||||||
Buffer fault_buffer;
|
|
||||||
std::shared_mutex slot_buffers_mutex;
|
|
||||||
Common::SlotVector<Buffer> slot_buffers;
|
Common::SlotVector<Buffer> slot_buffers;
|
||||||
u64 total_used_memory = 0;
|
u64 total_used_memory = 0;
|
||||||
u64 trigger_gc_memory = 0;
|
u64 trigger_gc_memory = 0;
|
||||||
@ -235,9 +223,6 @@ private:
|
|||||||
RangeSet gpu_modified_ranges;
|
RangeSet gpu_modified_ranges;
|
||||||
SplitRangeMap<BufferId> buffer_ranges;
|
SplitRangeMap<BufferId> buffer_ranges;
|
||||||
PageTable page_table;
|
PageTable page_table;
|
||||||
vk::UniqueDescriptorSetLayout fault_process_desc_layout;
|
|
||||||
vk::UniquePipeline fault_process_pipeline;
|
|
||||||
vk::UniquePipelineLayout fault_process_pipeline_layout;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace VideoCore
|
} // 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 {
|
layout(std430, binding = 1) buffer output_buf {
|
||||||
uint64_t download_buffer[];
|
uint64_t download_buffer[];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Overlap for 32 bit atomics
|
|
||||||
layout(std430, binding = 1) buffer output_buf32 {
|
layout(std430, binding = 1) buffer output_buf32 {
|
||||||
uint download_buffer32[];
|
uint download_buffer32[];
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(constant_id = 0) const uint CACHING_PAGEBITS = 0;
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
uint id = gl_GlobalInvocationID.x;
|
const uint id = gl_GlobalInvocationID.x;
|
||||||
uint word = fault_buffer[id];
|
uint word = fault_buffer[id];
|
||||||
if (word == 0u) {
|
fault_buffer[id] = 0u;
|
||||||
return;
|
const uint base_bit = id * 32u;
|
||||||
}
|
|
||||||
// 1 page per bit
|
|
||||||
uint base_bit = id * 32u;
|
|
||||||
while (word != 0u) {
|
while (word != 0u) {
|
||||||
uint bit = findLSB(word);
|
const uint store_index = atomicAdd(download_buffer32[0], 1u) + 1u;
|
||||||
word &= word - 1;
|
if (store_index >= MAX_PAGE_FAULTS) {
|
||||||
uint page = base_bit + bit;
|
return;
|
||||||
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 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
|
// Fill color target information
|
||||||
key.color_buffers[cb] = Shader::PsColorBuffer{
|
auto& color_buffer = key.color_buffers[cb];
|
||||||
.data_format = col_buf.GetDataFmt(),
|
color_buffer.data_format = col_buf.GetDataFmt();
|
||||||
.num_format = col_buf.GetNumberFmt(),
|
color_buffer.num_format = col_buf.GetNumberFmt();
|
||||||
.num_conversion = col_buf.GetNumberConversion(),
|
color_buffer.num_conversion = col_buf.GetNumberConversion();
|
||||||
.export_format = regs.color_export_format.GetFormat(cb),
|
color_buffer.export_format = regs.color_export_format.GetFormat(cb);
|
||||||
.swizzle = col_buf.Swizzle(),
|
color_buffer.swizzle = col_buf.Swizzle();
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile and bind shader stages
|
// Compile and bind shader stages
|
||||||
@ -379,7 +378,7 @@ bool PipelineCache::RefreshGraphicsKey() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((key.mrt_mask & (1u << cb)) == 0) {
|
if ((key.mrt_mask & (1u << cb)) == 0) {
|
||||||
key.color_buffers[cb] = {};
|
std::memset(&key.color_buffers[cb], 0, sizeof(Shader::PsColorBuffer));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -632,7 +631,7 @@ void PipelineCache::DumpShader(std::span<const u32> code, u64 hash, Shader::Stag
|
|||||||
std::filesystem::create_directories(dump_dir);
|
std::filesystem::create_directories(dump_dir);
|
||||||
}
|
}
|
||||||
const auto filename = fmt::format("{}.{}", GetShaderName(stage, hash, perm_idx), ext);
|
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);
|
file.WriteSpan(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -380,7 +380,8 @@ void Rasterizer::OnSubmit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Rasterizer::BindResources(const Pipeline* pipeline) {
|
bool Rasterizer::BindResources(const Pipeline* pipeline) {
|
||||||
if (IsComputeImageCopy(pipeline) || IsComputeMetaClear(pipeline)) {
|
if (IsComputeImageCopy(pipeline) || IsComputeMetaClear(pipeline) ||
|
||||||
|
IsComputeImageClear(pipeline)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,18 +407,13 @@ bool Rasterizer::BindResources(const Pipeline* pipeline) {
|
|||||||
|
|
||||||
if (uses_dma) {
|
if (uses_dma) {
|
||||||
// We only use fault buffer for DMA right now.
|
// We only use fault buffer for DMA right now.
|
||||||
{
|
Common::RecursiveSharedLock lock{mapped_ranges_mutex};
|
||||||
Common::RecursiveSharedLock lock{mapped_ranges_mutex};
|
for (auto& range : mapped_ranges) {
|
||||||
for (auto& range : mapped_ranges) {
|
buffer_cache.SynchronizeBuffersInRange(range.lower(), range.upper() - range.lower());
|
||||||
buffer_cache.SynchronizeBuffersInRange(range.lower(),
|
|
||||||
range.upper() - range.lower());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
buffer_cache.MemoryBarrier();
|
fault_process_pending = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fault_process_pending |= uses_dma;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,6 +516,66 @@ bool Rasterizer::IsComputeImageCopy(const Pipeline* pipeline) {
|
|||||||
return true;
|
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,
|
void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Bindings& binding,
|
||||||
Shader::PushData& push_data) {
|
Shader::PushData& push_data) {
|
||||||
buffer_bindings.clear();
|
buffer_bindings.clear();
|
||||||
|
|||||||
@ -112,6 +112,7 @@ private:
|
|||||||
|
|
||||||
bool IsComputeMetaClear(const Pipeline* pipeline);
|
bool IsComputeMetaClear(const Pipeline* pipeline);
|
||||||
bool IsComputeImageCopy(const Pipeline* pipeline);
|
bool IsComputeImageCopy(const Pipeline* pipeline);
|
||||||
|
bool IsComputeImageClear(const Pipeline* pipeline);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class VideoCore::BufferCache;
|
friend class VideoCore::BufferCache;
|
||||||
|
|||||||
@ -84,15 +84,6 @@ void Scheduler::Wait(u64 tick) {
|
|||||||
Flush(info);
|
Flush(info);
|
||||||
}
|
}
|
||||||
master_semaphore.Wait(tick);
|
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() {
|
void Scheduler::PopPendingOperations() {
|
||||||
@ -109,9 +100,7 @@ void Scheduler::AllocateWorkerCommandBuffers() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
current_cmdbuf = command_pool.Commit();
|
current_cmdbuf = command_pool.Commit();
|
||||||
auto begin_result = current_cmdbuf.begin(begin_info);
|
Check(current_cmdbuf.begin(begin_info));
|
||||||
ASSERT_MSG(begin_result == vk::Result::eSuccess, "Failed to begin command buffer: {}",
|
|
||||||
vk::to_string(begin_result));
|
|
||||||
|
|
||||||
// Invalidate dynamic state so it gets applied to the new command buffer.
|
// Invalidate dynamic state so it gets applied to the new command buffer.
|
||||||
dynamic_state.Invalidate();
|
dynamic_state.Invalidate();
|
||||||
@ -139,9 +128,7 @@ void Scheduler::SubmitExecution(SubmitInfo& info) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
EndRendering();
|
EndRendering();
|
||||||
auto end_result = current_cmdbuf.end();
|
Check(current_cmdbuf.end());
|
||||||
ASSERT_MSG(end_result == vk::Result::eSuccess, "Failed to end command buffer: {}",
|
|
||||||
vk::to_string(end_result));
|
|
||||||
|
|
||||||
const vk::Semaphore timeline = master_semaphore.Handle();
|
const vk::Semaphore timeline = master_semaphore.Handle();
|
||||||
info.AddSignal(timeline, signal_value);
|
info.AddSignal(timeline, signal_value);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user