diff --git a/.gitmodules b/.gitmodules index 38aed89a0..b8d1544e4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -108,8 +108,12 @@ branch = dist [submodule "externals/MoltenVK"] path = externals/MoltenVK - url = https://github.com/KhronosGroup/MoltenVK.git + url = https://github.com/shadPS4-emu/ext-MoltenVK.git shallow = true [submodule "externals/json"] path = externals/json url = https://github.com/nlohmann/json.git +[submodule "externals/sdl3_mixer"] + path = externals/sdl3_mixer + url = https://github.com/libsdl-org/SDL_mixer + shallow = true diff --git a/CMakeLists.txt b/CMakeLists.txt index a4afd5d44..36cea49f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,7 +203,7 @@ execute_process( # Set Version set(EMULATOR_VERSION_MAJOR "0") set(EMULATOR_VERSION_MINOR "12") -set(EMULATOR_VERSION_PATCH "1") +set(EMULATOR_VERSION_PATCH "6") set_source_files_properties(src/shadps4.rc PROPERTIES COMPILE_DEFINITIONS "EMULATOR_VERSION_MAJOR=${EMULATOR_VERSION_MAJOR};EMULATOR_VERSION_MINOR=${EMULATOR_VERSION_MINOR};EMULATOR_VERSION_PATCH=${EMULATOR_VERSION_PATCH}") @@ -229,6 +229,7 @@ find_package(magic_enum 0.9.7 CONFIG) find_package(PNG 1.6 MODULE) find_package(RenderDoc 1.6.0 MODULE) find_package(SDL3 3.1.2 CONFIG) +find_package(SDL3_mixer 2.8.1 CONFIG) find_package(stb MODULE) find_package(toml11 4.2.0 CONFIG) find_package(tsl-robin-map 1.3.0 CONFIG) @@ -463,6 +464,12 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/mouse/mouse.h src/core/libraries/web_browser_dialog/webbrowserdialog.cpp src/core/libraries/web_browser_dialog/webbrowserdialog.h + src/core/libraries/font/font.cpp + src/core/libraries/font/font.h + src/core/libraries/font/fontft.cpp + src/core/libraries/font/fontft.h + src/core/libraries/font/font_error.h + ) set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h @@ -535,6 +542,10 @@ set(RANDOM_LIB src/core/libraries/random/random.cpp set(USBD_LIB src/core/libraries/usbd/usbd.cpp src/core/libraries/usbd/usbd.h src/core/libraries/usbd/usb_backend.h + src/core/libraries/usbd/emulated/dimensions.cpp + src/core/libraries/usbd/emulated/dimensions.h + src/core/libraries/usbd/emulated/infinity.cpp + src/core/libraries/usbd/emulated/infinity.h src/core/libraries/usbd/emulated/skylander.cpp src/core/libraries/usbd/emulated/skylander.h ) @@ -951,6 +962,8 @@ set(VIDEO_CORE src/video_core/amdgpu/cb_db_extent.h src/video_core/buffer_cache/buffer.h src/video_core/buffer_cache/buffer_cache.cpp src/video_core/buffer_cache/buffer_cache.h + src/video_core/buffer_cache/fault_manager.cpp + src/video_core/buffer_cache/fault_manager.h src/video_core/buffer_cache/memory_tracker.h src/video_core/buffer_cache/range_set.h src/video_core/buffer_cache/region_definitions.h @@ -1062,7 +1075,7 @@ add_executable(shadps4 create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG) -target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json) +target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 SDL3_mixer::SDL3_mixer pugixml::pugixml stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json) target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h") target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h") diff --git a/dist/net.shadps4.shadPS4.metainfo.xml b/dist/net.shadps4.shadPS4.metainfo.xml index ae07e79d8..5798876f6 100644 --- a/dist/net.shadps4.shadPS4.metainfo.xml +++ b/dist/net.shadps4.shadPS4.metainfo.xml @@ -37,6 +37,9 @@ Game + + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.12.5 + https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.12.0 diff --git a/documents/building-windows.md b/documents/building-windows.md index aa7213abc..88c5b6830 100644 --- a/documents/building-windows.md +++ b/documents/building-windows.md @@ -36,16 +36,11 @@ Go through the Git for Windows installation as normal 1. Open up Visual Studio, select `Open a local folder` and select the folder with the shadPS4 source code. The folder should contain `CMakeLists.txt` 2. Change Clang x64 Debug to Clang x64 Release if you want a regular, non-debug build. -3. If you want to build shadPS4 with the Qt Gui, simply select Clang x64 Release with Qt instead. -4. Change the project to build to shadps4.exe -5. Build -> Build All +3. Change the project to build to shadps4.exe +4. Build -> Build All Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\` -To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal: -`C:\Qt\\msvc2022_64\bin\windeployqt6.exe "C:\path\to\shadps4.exe"` -(Change Qt path if you've installed it to non-default path) - ## Option 2: MSYS2/MinGW > [!IMPORTANT] @@ -73,7 +68,6 @@ ARM64-based computers, follow: 1. Open "MSYS2 CLANGARM64" from your new applications 2. Run `pacman -Syu`, let it complete; 3. Run `pacman -S --needed git mingw-w64-clang-aarch64-binutils mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-rapidjson mingw-w64-clang-aarch64-cmake mingw-w64-clang-aarch64-ninja mingw-w64-clang-aarch64-ffmpeg` - 1. Optional (Qt only): run `pacman -S --needed mingw-w64-clang-aarch64-qt6-base mingw-w64-clang-aarch64-qt6-tools mingw-w64-clang-aarch64-qt6-multimedia` 4. Run `git clone --depth 1 --recursive https://github.com/shadps4-emu/shadPS4` 5. Run `cd shadPS4` 6. Run `cmake -S . -B build -DCMAKE_C_COMPILER="clang.exe" -DCMAKE_CXX_COMPILER="clang++.exe" -DCMAKE_CXX_FLAGS="-O2 -march=native"` diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index a26f58d69..5f7ae94c4 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -63,6 +63,18 @@ if (NOT TARGET SDL3::SDL3) add_subdirectory(sdl3) endif() +# SDL3_mixer +if (NOT TARGET SDL3_mixer::SDL3_mixer) + set(SDLMIXER_FLAC OFF) + set(SDLMIXER_OGG OFF) + set(SDLMIXER_MOD OFF) + set(SDLMIXER_MIDI OFF) + set(SDLMIXER_OPUS OFF) + set(SDLMIXER_WAVPACK OFF) + set(BUILD_SHARED_LIBS OFF) + add_subdirectory(sdl3_mixer) +endif() + # vulkan-headers if (NOT TARGET Vulkan::Headers) set(VULKAN_HEADERS_ENABLE_MODULE OFF) diff --git a/externals/MoltenVK b/externals/MoltenVK index b23d42534..f168dec05 160000 --- a/externals/MoltenVK +++ b/externals/MoltenVK @@ -1 +1 @@ -Subproject commit b23d42534622cd9926fe526fec1b7f8795a2853c +Subproject commit f168dec05998ab0ca09a400bab6831a95c0bdb2e diff --git a/externals/sdl3_mixer b/externals/sdl3_mixer new file mode 160000 index 000000000..4182794ea --- /dev/null +++ b/externals/sdl3_mixer @@ -0,0 +1 @@ +Subproject commit 4182794ea45fe28568728670c6f1583855d0e85c diff --git a/shell.nix b/shell.nix index 50336d1b2..611095268 100644 --- a/shell.nix +++ b/shell.nix @@ -6,42 +6,43 @@ with import (fetchTarball "https://github.com/nixos/nixpkgs/archive/cfd19cdc5468 pkgs.mkShell { name = "shadps4-build-env"; - nativeBuildInputs = [ - pkgs.llvmPackages_18.clang - pkgs.cmake - pkgs.pkg-config - pkgs.git + nativeBuildInputs = with pkgs; [ + llvmPackages_18.clang + cmake + pkg-config + git + util-linux ]; - buildInputs = [ - pkgs.alsa-lib - pkgs.libpulseaudio - pkgs.openal - pkgs.zlib - pkgs.libedit - pkgs.udev - pkgs.libevdev - pkgs.SDL2 - pkgs.jack2 - pkgs.sndio + buildInputs = with pkgs; [ + alsa-lib + libpulseaudio + openal + zlib + libedit + udev + libevdev + SDL2 + jack2 + sndio - pkgs.vulkan-headers - pkgs.vulkan-utility-libraries - pkgs.vulkan-tools + vulkan-headers + vulkan-utility-libraries + vulkan-tools - pkgs.ffmpeg - pkgs.fmt - pkgs.glslang - pkgs.libxkbcommon - pkgs.wayland - pkgs.xorg.libxcb - pkgs.xorg.xcbutil - pkgs.xorg.xcbutilkeysyms - pkgs.xorg.xcbutilwm - pkgs.sdl3 - pkgs.stb - pkgs.wayland-protocols - pkgs.libpng + ffmpeg + fmt + glslang + libxkbcommon + wayland + xorg.libxcb + xorg.xcbutil + xorg.xcbutilkeysyms + xorg.xcbutilwm + sdl3 + stb + wayland-protocols + libpng ]; shellHook = '' diff --git a/src/common/elf_info.h b/src/common/elf_info.h index 2f2f150b2..0b2589e95 100644 --- a/src/common/elf_info.h +++ b/src/common/elf_info.h @@ -68,6 +68,7 @@ class ElfInfo { std::string app_ver{}; u32 firmware_ver = 0; u32 raw_firmware_ver = 0; + u32 sdk_ver = 0; PSFAttributes psf_attributes{}; std::filesystem::path splash_path{}; @@ -117,6 +118,11 @@ public: return raw_firmware_ver; } + [[nodiscard]] u32 CompiledSdkVer() const { + ASSERT(initialized); + return sdk_ver; + } + [[nodiscard]] const PSFAttributes& GetPSFAttributes() const { ASSERT(initialized); return psf_attributes; diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp index 6fa9062a7..249d0e0f4 100644 --- a/src/common/io_file.cpp +++ b/src/common/io_file.cpp @@ -40,28 +40,30 @@ namespace { switch (mode) { case FileAccessMode::Read: return L"rb"; - case FileAccessMode::Write: - return L"wb"; case FileAccessMode::Append: return L"ab"; + case FileAccessMode::Write: case FileAccessMode::ReadWrite: return L"r+b"; case FileAccessMode::ReadAppend: return L"a+b"; + case FileAccessMode::Create: + return L"wb"; } break; case FileType::TextFile: switch (mode) { case FileAccessMode::Read: return L"r"; - case FileAccessMode::Write: - return L"w"; case FileAccessMode::Append: return L"a"; + case FileAccessMode::Write: case FileAccessMode::ReadWrite: return L"r+"; case FileAccessMode::ReadAppend: return L"a+"; + case FileAccessMode::Create: + return L"w"; } break; } @@ -91,28 +93,30 @@ namespace { switch (mode) { case FileAccessMode::Read: return "rb"; - case FileAccessMode::Write: - return "wb"; case FileAccessMode::Append: return "ab"; + case FileAccessMode::Write: case FileAccessMode::ReadWrite: return "r+b"; case FileAccessMode::ReadAppend: return "a+b"; + case FileAccessMode::Create: + return "wb"; } break; case FileType::TextFile: switch (mode) { case FileAccessMode::Read: return "r"; - case FileAccessMode::Write: - return "w"; case FileAccessMode::Append: return "a"; + case FileAccessMode::Write: case FileAccessMode::ReadWrite: return "r+"; case FileAccessMode::ReadAppend: return "a+"; + case FileAccessMode::Create: + return "w"; } break; } diff --git a/src/common/io_file.h b/src/common/io_file.h index c6eb3b563..d4e4b2e47 100644 --- a/src/common/io_file.h +++ b/src/common/io_file.h @@ -21,9 +21,8 @@ enum class FileAccessMode { */ Read = 1 << 0, /** - * If the file at path exists, the existing contents of the file are erased. - * The empty file is then opened for writing. - * If the file at path does not exist, it creates and opens a new empty file for writing. + * If the file at path exists, it opens the file for writing. + * If the file at path does not exist, it fails to open the file. */ Write = 1 << 1, /** @@ -42,6 +41,12 @@ enum class FileAccessMode { * reading and appending. */ ReadAppend = Read | Append, + /** + * If the file at path exists, the existing contents of the file are erased. + * The empty file is then opened for writing. + * If the file at path does not exist, it creates and opens a new empty file for writing. + */ + Create = 1 << 3, }; DECLARE_ENUM_FLAG_OPERATORS(FileAccessMode); @@ -102,6 +107,11 @@ public: return file != nullptr; } + bool IsWriteOnly() const { + return file_access_mode == FileAccessMode::Append || + file_access_mode == FileAccessMode::Write; + } + uintptr_t GetFileMapping(); int Open(const std::filesystem::path& path, FileAccessMode mode, @@ -210,7 +220,7 @@ public: } static size_t WriteBytes(const std::filesystem::path path, const auto& data) { - IOFile out(path, FileAccessMode::Write); + IOFile out(path, FileAccessMode::Create); return out.Write(data); } std::FILE* file = nullptr; diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index ce9386853..4a85c4cde 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -62,7 +62,7 @@ private: class FileBackend { public: explicit FileBackend(const std::filesystem::path& filename, bool should_append = false) - : file{filename, should_append ? FS::FileAccessMode::Append : FS::FileAccessMode::Write, + : file{filename, should_append ? FS::FileAccessMode::Append : FS::FileAccessMode::Create, FS::FileType::TextFile} {} ~FileBackend() = default; @@ -182,7 +182,13 @@ public: } void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, - const char* function, std::string message) { + const char* function, const char* format, const fmt::format_args& args) { + if (!filter.CheckMessage(log_class, log_level) || !Config::getLoggingEnabled()) { + return; + } + + const auto message = fmt::vformat(format, args); + // Propagate important log messages to the profiler if (IsProfilerConnected()) { const auto& msg_str = fmt::format("[{}] {}", GetLogClassName(log_class), message); @@ -201,10 +207,6 @@ public: } } - if (!filter.CheckMessage(log_class, log_level) || !Config::getLoggingEnabled()) { - return; - } - using std::chrono::duration_cast; using std::chrono::microseconds; using std::chrono::steady_clock; @@ -324,8 +326,8 @@ void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, const char* format, const fmt::format_args& args) { if (!initialization_in_progress_suppress_logging) [[likely]] { - Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, - fmt::vformat(format, args)); + Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, format, + args); } } } // namespace Common::Log diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 2f98f07a0..bf6844c7d 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -140,6 +140,8 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, NpParty) \ SUB(Lib, Zlib) \ SUB(Lib, Hmd) \ + SUB(Lib, Font) \ + SUB(Lib, FontFt) \ SUB(Lib, HmdSetupDialog) \ SUB(Lib, SigninDialog) \ SUB(Lib, Camera) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index eee9d4ca2..035a959db 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -114,6 +114,8 @@ enum class Class : u8 { Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation. Lib_CompanionUtil, ///< The LibCompanionUtil implementation. Lib_VrTracker, ///< The LibSceVrTracker implementation. + Lib_Font, ///< The libSceFont implementation. + Lib_FontFt, ///< The libSceFontFt implementation. Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/core/address_space.cpp b/src/core/address_space.cpp index 3f2d94cbf..3f063ea76 100644 --- a/src/core/address_space.cpp +++ b/src/core/address_space.cpp @@ -6,6 +6,7 @@ #include "common/arch.h" #include "common/assert.h" #include "common/config.h" +#include "common/elf_info.h" #include "common/error.h" #include "core/address_space.h" #include "core/libraries/kernel/memory.h" @@ -103,8 +104,8 @@ struct AddressSpace::Impl { GetSystemInfo(&sys_info); u64 alignment = sys_info.dwAllocationGranularity; - // Determine the host OS build number - // Retrieve module handle for ntdll + // Older Windows builds have a severe performance issue with VirtualAlloc2. + // We need to get the host's Windows version, then determine if it needs a workaround. auto ntdll_handle = GetModuleHandleW(L"ntdll.dll"); ASSERT_MSG(ntdll_handle, "Failed to retrieve ntdll handle"); @@ -120,12 +121,20 @@ struct AddressSpace::Impl { u64 supported_user_max = USER_MAX; // This is the build number for Windows 11 22H2 static constexpr s32 AffectedBuildNumber = 22621; - if (os_version_info.dwBuildNumber <= AffectedBuildNumber) { - // Older Windows builds have an issue with VirtualAlloc2 on higher addresses. - // To prevent regressions, limit the maximum address we reserve for this platform. - supported_user_max = 0x11000000000ULL; - LOG_WARNING(Core, "Windows 10 detected, reducing user max to {:#x} to avoid problems", - supported_user_max); + + // Higher PS4 firmware versions prevent higher address mappings too. + s32 sdk_ver = Common::ElfInfo::Instance().CompiledSdkVer(); + if (os_version_info.dwBuildNumber <= AffectedBuildNumber || + sdk_ver >= Common::ElfInfo::FW_30) { + supported_user_max = 0x10000000000ULL; + // Only log the message if we're restricting the user max due to operating system. + // Since higher compiled SDK versions also get reduced max, we don't need to log there. + if (sdk_ver < Common::ElfInfo::FW_30) { + LOG_WARNING( + Core, + "Older Windows version detected, reducing user max to {:#x} to avoid problems", + supported_user_max); + } } // Determine the free address ranges we can access. diff --git a/src/core/devtools/widget/common.h b/src/core/devtools/widget/common.h index 4684f6e3b..1c79ffcac 100644 --- a/src/core/devtools/widget/common.h +++ b/src/core/devtools/widget/common.h @@ -152,7 +152,7 @@ inline std::string RunDisassembler(const std::string& disassembler_cli, const T& } } else { cli.replace(pos, src_arg.size(), "\"" + bin_path.string() + "\""); - Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Write); + Common::FS::IOFile file(bin_path, Common::FS::FileAccessMode::Create); file.Write(shader_code); file.Close(); diff --git a/src/core/devtools/widget/frame_dump.cpp b/src/core/devtools/widget/frame_dump.cpp index 1eaa78897..06b65b0ba 100644 --- a/src/core/devtools/widget/frame_dump.cpp +++ b/src/core/devtools/widget/frame_dump.cpp @@ -123,7 +123,7 @@ void FrameDumpViewer::Draw() { const auto fname = fmt::format("{:%F %H-%M-%S} {}_{}_{}.bin", now_time, magic_enum::enum_name(selected_queue_type), selected_submit_num, selected_queue_num2); - Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Write); + Common::FS::IOFile file(fname, Common::FS::FileAccessMode::Create); const auto& data = frame_dump->queues[selected_cmd].data; if (file.IsOpen()) { DebugState.ShowDebugMessage(fmt::format("Dumping cmd as {}", fname)); diff --git a/src/core/file_format/psf.cpp b/src/core/file_format/psf.cpp index 7e0ffc9a3..047828330 100644 --- a/src/core/file_format/psf.cpp +++ b/src/core/file_format/psf.cpp @@ -99,7 +99,7 @@ bool PSF::Open(const std::vector& psf_buffer) { } bool PSF::Encode(const std::filesystem::path& filepath) const { - Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Write); + Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Create); if (!file.IsOpen()) { return false; } diff --git a/src/core/libraries/font/font.cpp b/src/core/libraries/font/font.cpp new file mode 100644 index 000000000..1454004aa --- /dev/null +++ b/src/core/libraries/font/font.cpp @@ -0,0 +1,1611 @@ +// 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/font.h" +#include "core/libraries/libs.h" +#include "font_error.h" + +namespace Libraries::Font { + +s32 PS4_SYSV_ABI sceFontAttachDeviceCacheBuffer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontBindRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterGetBidiLevel(OrbisFontTextCharacter* textCharacter, + int* bidiLevel) { + if (!textCharacter || !bidiLevel) { + LOG_DEBUG(Lib_Font, "Invalid parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + *bidiLevel = textCharacter->bidiLevel; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterGetSyllableStringState() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterGetTextFontCode() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCharacterGetTextOrder(OrbisFontTextCharacter* textCharacter, + void** pTextOrder) { + if (!pTextOrder) { + LOG_DEBUG(Lib_Font, "Invalid parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + if (!textCharacter) { + LOG_DEBUG(Lib_Font, "Invalid parameter"); + *pTextOrder = NULL; + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Retrieve text order + *pTextOrder = textCharacter->textOrder; + return ORBIS_OK; +} + +u32 PS4_SYSV_ABI sceFontCharacterLooksFormatCharacters(OrbisFontTextCharacter* textCharacter) { + if (!textCharacter) { + return 0; + } + + // Check if the format flag (bit 2) is set + return (textCharacter->formatFlags & 0x04) ? textCharacter->characterCode : 0; +} + +u32 PS4_SYSV_ABI sceFontCharacterLooksWhiteSpace(OrbisFontTextCharacter* textCharacter) { + if (!textCharacter) { + return 0; + } + + return (textCharacter->charType == 0x0E) ? textCharacter->characterCode : 0; +} + +OrbisFontTextCharacter* PS4_SYSV_ABI +sceFontCharacterRefersTextBack(OrbisFontTextCharacter* textCharacter) { + if (!textCharacter) + return NULL; // Check if input is NULL + + OrbisFontTextCharacter* current = textCharacter->prev; // Move backward instead of forward + while (current) { + if (current->unkn_0x31 == 0 && current->unkn_0x33 == 0) { + return current; // Return the first matching node + } + current = current->prev; // Move to the previous node + } + + return NULL; // No valid node found +} + +OrbisFontTextCharacter* PS4_SYSV_ABI +sceFontCharacterRefersTextNext(OrbisFontTextCharacter* textCharacter) { + if (!textCharacter) + return NULL; // Null check + + OrbisFontTextCharacter* current = textCharacter->next; + while (current) { + if (current->unkn_0x31 == 0 && current->unkn_0x33 == 0) { + return current; // Found a match + } + current = current->next; // Move to the next node + } + + return NULL; // No matching node found +} + +s32 PS4_SYSV_ABI sceFontCharactersRefersTextCodes() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontClearDeviceCache() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCloseFont() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontControl() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateGraphicsDevice() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateGraphicsService() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateGraphicsServiceWithEdition() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateLibrary() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateLibraryWithEdition() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateRendererWithEdition() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateString() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateWords() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontCreateWritingLine() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDefineAttribute() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDeleteGlyph() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyGraphicsDevice() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyGraphicsService() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyLibrary() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyString() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyWords() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDestroyWritingLine() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontDettachDeviceCacheBuffer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGenerateCharGlyph() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetAttribute() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetCharGlyphCode() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetCharGlyphMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontGlyphsCount() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontGlyphsOutlineProfile() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontResolution() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetFontStyleInformation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetGlyphExpandBufferState() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetHorizontalLayout() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetKerning() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetLibrary() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetPixelResolution() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderCharGlyphMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderScaledKerning() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetRenderScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetResolutionDpi() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetScriptLanguage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetTypographicDesign() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGetVerticalLayout() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphDefineAttribute() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphGetAttribute() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphGetGlyphForm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphGetMetricsForm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphGetScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontal() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalAdvance() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersMetricsHorizontalX() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRefersOutline() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRenderImage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRenderImageHorizontal() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGlyphRenderImageVertical() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsBeginFrame() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsDrawingCancel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsDrawingFinish() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsEndFrame() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsExchangeResource() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillMethodInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetLayout() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillPlotSetMapping() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetFillEffect() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetLayout() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsFillRatesSetMapping() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsGetDeviceUsage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRegionInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRegionInitCircular() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRegionInitRoundish() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRelease() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsRenderResource() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetFramePolicy() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupClipping() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupColorRates() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupFillMethod() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupFillRates() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFill() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupGlyphFillPlot() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupHandleDefault() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupLocation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupPositioning() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupRotation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupScaling() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFill() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsSetupShapeFillPlot() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvas() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureCanvasSequence() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureDesign() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureDesignResource() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsStructureSurfaceTexture() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateClipping() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateColorRates() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillMethod() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateFillRates() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFill() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateGlyphFillPlot() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateLocation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdatePositioning() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateRotation() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateScaling() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFill() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontGraphicsUpdateShapeFillPlot() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontMemoryInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontMemoryTerm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontOpenFontFile() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontOpenFontInstance() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontOpenFontMemory() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontOpenFontSet() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRebindRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageHorizontal() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRenderCharGlyphImageVertical() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRendererGetOutlineBufferSize() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRendererResetOutlineBuffer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontRendererSetOutlineBufferPolicy() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +void PS4_SYSV_ABI sceFontRenderSurfaceInit(OrbisFontRenderSurface* renderSurface, void* buffer, + int bufWidthByte, int pixelSizeByte, int widthPixel, + int heightPixel) { + if (renderSurface) { // Ensure surface is not NULL before modifying it + renderSurface->buffer = buffer; + renderSurface->widthByte = bufWidthByte; + renderSurface->pixelSizeByte = pixelSizeByte; + + // Initialize unknown fields (likely reserved or flags) + renderSurface->unkn_0xd = 0; + renderSurface->styleFlag = 0; + renderSurface->unkn_0xf = 0; + + // Ensure width and height are non-negative + renderSurface->width = (widthPixel < 0) ? 0 : widthPixel; + renderSurface->height = (heightPixel < 0) ? 0 : heightPixel; + + // Set the clipping/scaling rectangle + renderSurface->sc_x0 = 0; + renderSurface->sc_y0 = 0; + renderSurface->sc_x1 = renderSurface->width; + renderSurface->sc_y1 = renderSurface->height; + } +} + +void PS4_SYSV_ABI sceFontRenderSurfaceSetScissor(OrbisFontRenderSurface* renderSurface, int x0, + int y0, int w, int h) { + if (!renderSurface) + return; // Null check + + // Handle horizontal clipping + int surfaceWidth = renderSurface->width; + int clip_x0, clip_x1; + + if (surfaceWidth != 0) { + if (x0 < 0) { // Adjust for negative x0 + clip_x0 = 0; + clip_x1 = (w + x0 > surfaceWidth) ? surfaceWidth : w + x0; + if (w <= -x0) + clip_x1 = 0; // Entire width is clipped + } else { + clip_x0 = (x0 > surfaceWidth) ? surfaceWidth : x0; + clip_x1 = (w + x0 > surfaceWidth) ? surfaceWidth : w + x0; + } + renderSurface->sc_x0 = clip_x0; + renderSurface->sc_x1 = clip_x1; + } + + // Handle vertical clipping + int surfaceHeight = renderSurface->height; + int clip_y0, clip_y1; + + if (surfaceHeight != 0) { + if (y0 < 0) { // Adjust for negative y0 + clip_y0 = 0; + clip_y1 = (h + y0 > surfaceHeight) ? surfaceHeight : h + y0; + if (h <= -y0) + clip_y1 = 0; // Entire height is clipped + } else { + clip_y0 = (y0 > surfaceHeight) ? surfaceHeight : y0; + clip_y1 = (h + y0 > surfaceHeight) ? surfaceHeight : h + y0; + } + renderSurface->sc_y0 = clip_y0; + renderSurface->sc_y1 = clip_y1; + } +} + +s32 PS4_SYSV_ABI sceFontRenderSurfaceSetStyleFrame(OrbisFontRenderSurface* renderSurface, + OrbisFontStyleFrame* styleFrame) { + if (!renderSurface) { + LOG_ERROR(Lib_Font, "Invalid Parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + if (!styleFrame) { + renderSurface->styleFlag &= 0xFE; // Clear style flag + } else { + // Validate magic number + if (styleFrame->magic != 0xF09) { + LOG_ERROR(Lib_Font, "Invalid magic"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + renderSurface->styleFlag |= 1; // Set style flag + } + + // Assign style frame pointer + renderSurface->unkn_28[0] = styleFrame; + *(uint32_t*)(renderSurface->unkn_28 + 1) = 0; // Reset related field + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetFontsOpenMode() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetResolutionDpi() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetScriptLanguage() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetTypographicDesign() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetupRenderEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetupRenderEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetupRenderScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSetupRenderScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringGetTerminateCode() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringGetTerminateOrder() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringGetWritingForm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringRefersRenderCharacters() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStringRefersTextCharacters() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectSlant(OrbisFontStyleFrame* styleFrame, + float* slantRatio) { + if (!styleFrame) { + LOG_ERROR(Lib_Font, "Invalid Parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Validate the magic number + if (styleFrame->magic != 0xF09) { + LOG_ERROR(Lib_Font, "Invalid Magic"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Check if the slant effect is enabled (bit 1 in flags) + if (!(styleFrame->flags & 0x02)) { + LOG_ERROR(Lib_Font, "Flag not set"); + return ORBIS_FONT_ERROR_UNSET_PARAMETER; + } + + if (!slantRatio) { + LOG_ERROR(Lib_Font, "Invalid Parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Retrieve slant ratio + *slantRatio = styleFrame->slantRatio; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetEffectWeight(OrbisFontStyleFrame* fontStyleFrame, + float* weightXScale, float* weightYScale, + uint32_t* mode) { + if (!fontStyleFrame) { + LOG_ERROR(Lib_Font, "Invalid Parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Validate the magic number + if (fontStyleFrame->magic != 0xF09) { + LOG_ERROR(Lib_Font, "Magic not set"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + // Check if the weight effect is enabled (bit 2 in flags) + if (!(fontStyleFrame->flags & 0x04)) { + LOG_ERROR(Lib_Font, "Flag not set"); + return ORBIS_FONT_ERROR_UNSET_PARAMETER; + } + + // Retrieve weight scales (default is +1.0 to maintain normal weight) + if (weightXScale) { + *weightXScale = fontStyleFrame->weightXScale + 1.0f; + } + if (weightYScale) { + *weightYScale = fontStyleFrame->weightYScale + 1.0f; + } + + // Reset mode if provided + if (mode) { + *mode = 0; + } + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetResolutionDpi() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePixel(OrbisFontStyleFrame* styleFrame, float* w, + float* h) { + if (!styleFrame) { + LOG_ERROR(Lib_Font, "Invalid Parameter"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + if (styleFrame->magic != 0xF09) { + LOG_ERROR(Lib_Font, "Invalid magic"); + return ORBIS_FONT_ERROR_INVALID_PARAMETER; + } + + if (!(styleFrame->flags & 0x01)) { + LOG_ERROR(Lib_Font, "Scaling effect parameter not set"); + return ORBIS_FONT_ERROR_UNSET_PARAMETER; + } + + // Check if scaling is allowed + int isScalingEnabled = styleFrame->scalingFlag; + if (w) { + *w = styleFrame->scaleWidth; + if (isScalingEnabled && styleFrame->dpiX) { + *w *= ((float)styleFrame->dpiX / 72.0f); + } + } + + if (h) { + *h = styleFrame->scaleHeight; + if (isScalingEnabled && styleFrame->dpiY) { + *h *= ((float)styleFrame->dpiY / 72.0f); + } + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameGetScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetResolutionDpi() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePixel() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameSetScalePoint() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectSlant() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetEffectWeight() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontStyleFrameUnsetScale() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSupportExternalFonts() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSupportGlyphs() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontSupportSystemFonts() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextCodesStepBack() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextCodesStepNext() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextSourceInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextSourceRewind() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextSourceSetDefaultFont() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontTextSourceSetWritingForm() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontUnbindRenderer() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWordsFindWordCharacters() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingGetRenderMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingInit() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineClear() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineGetOrderingSpace() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineGetRenderMetrics() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineRefersRenderStep() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingLineWritesOrder() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingRefersRenderStep() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingRefersRenderStepCharacter() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFontWritingSetMaskInvisible() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_00F4D778F1C88CB3() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_03C650025FBB0DE7() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_07EAB8A163B27E1A() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_09408E88E4F97CE3() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_09F92905ED82A814() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_0D142CEE1AB21ABE() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_14BD2E9E119C16F2() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_1AC53C9EDEAE8D75() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_1D401185D5E24C3D() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_1E83CD20C2CC996F() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_314B1F765B9FE78A() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_350E6725FEDE29E1() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_3DB773F0A604BF39() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_4FF49DD21E311B1C() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_526287664A493981() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_55CA718DBC84A6E9() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_563FC5F0706A8B4D() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_569E2ECD34290F45() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_5A04775B6BE47685() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_5FD93BCAB6F79750() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_62B5398F864BD3B4() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_6F9010294D822367() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_7757E947423A7A67() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_7E06BA52077F54FA() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_93B36DEA021311D6() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_94B0891E7111598A() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_9785C9128C2FE7CD() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_97DFBC9B65FBC0E1() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_ACD9717405D7D3CA() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_B19A8AEC3FD4F16F() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_C10F488AD7CF103D() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_D0C8B5FF4A6826C7() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_E48D3CD01C342A33() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_EAC96B2186B71E14() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_FE4788A96EF46256() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI Func_FE7E5AE95D3058F5() { + LOG_ERROR(Lib_Font, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceFont(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("CUKn5pX-NVY", "libSceFont", 1, "libSceFont", sceFontAttachDeviceCacheBuffer); + LIB_FUNCTION("3OdRkSjOcog", "libSceFont", 1, "libSceFont", sceFontBindRenderer); + LIB_FUNCTION("6DFUkCwQLa8", "libSceFont", 1, "libSceFont", sceFontCharacterGetBidiLevel); + LIB_FUNCTION("coCrV6IWplE", "libSceFont", 1, "libSceFont", + sceFontCharacterGetSyllableStringState); + LIB_FUNCTION("zN3+nuA0SFQ", "libSceFont", 1, "libSceFont", sceFontCharacterGetTextFontCode); + LIB_FUNCTION("mxgmMj-Mq-o", "libSceFont", 1, "libSceFont", sceFontCharacterGetTextOrder); + LIB_FUNCTION("-P6X35Rq2-E", "libSceFont", 1, "libSceFont", + sceFontCharacterLooksFormatCharacters); + LIB_FUNCTION("SaRlqtqaCew", "libSceFont", 1, "libSceFont", sceFontCharacterLooksWhiteSpace); + LIB_FUNCTION("6Gqlv5KdTbU", "libSceFont", 1, "libSceFont", sceFontCharacterRefersTextBack); + LIB_FUNCTION("BkjBP+YC19w", "libSceFont", 1, "libSceFont", sceFontCharacterRefersTextNext); + LIB_FUNCTION("lVSR5ftvNag", "libSceFont", 1, "libSceFont", sceFontCharactersRefersTextCodes); + LIB_FUNCTION("I9R5VC6eZWo", "libSceFont", 1, "libSceFont", sceFontClearDeviceCache); + LIB_FUNCTION("vzHs3C8lWJk", "libSceFont", 1, "libSceFont", sceFontCloseFont); + LIB_FUNCTION("MpKSBaYKluo", "libSceFont", 1, "libSceFont", sceFontControl); + LIB_FUNCTION("WBNBaj9XiJU", "libSceFont", 1, "libSceFont", sceFontCreateGraphicsDevice); + LIB_FUNCTION("4So0MC3oBIM", "libSceFont", 1, "libSceFont", sceFontCreateGraphicsService); + LIB_FUNCTION("NlO5Qlhjkng", "libSceFont", 1, "libSceFont", + sceFontCreateGraphicsServiceWithEdition); + LIB_FUNCTION("nWrfPI4Okmg", "libSceFont", 1, "libSceFont", sceFontCreateLibrary); + LIB_FUNCTION("n590hj5Oe-k", "libSceFont", 1, "libSceFont", sceFontCreateLibraryWithEdition); + LIB_FUNCTION("u5fZd3KZcs0", "libSceFont", 1, "libSceFont", sceFontCreateRenderer); + LIB_FUNCTION("WaSFJoRWXaI", "libSceFont", 1, "libSceFont", sceFontCreateRendererWithEdition); + LIB_FUNCTION("MO24vDhmS4E", "libSceFont", 1, "libSceFont", sceFontCreateString); + LIB_FUNCTION("cYrMGk1wrMA", "libSceFont", 1, "libSceFont", sceFontCreateWords); + LIB_FUNCTION("7rogx92EEyc", "libSceFont", 1, "libSceFont", sceFontCreateWritingLine); + LIB_FUNCTION("8h-SOB-asgk", "libSceFont", 1, "libSceFont", sceFontDefineAttribute); + LIB_FUNCTION("LHDoRWVFGqk", "libSceFont", 1, "libSceFont", sceFontDeleteGlyph); + LIB_FUNCTION("5QG71IjgOpQ", "libSceFont", 1, "libSceFont", sceFontDestroyGraphicsDevice); + LIB_FUNCTION("zZQD3EwJo3c", "libSceFont", 1, "libSceFont", sceFontDestroyGraphicsService); + LIB_FUNCTION("FXP359ygujs", "libSceFont", 1, "libSceFont", sceFontDestroyLibrary); + LIB_FUNCTION("exAxkyVLt0s", "libSceFont", 1, "libSceFont", sceFontDestroyRenderer); + LIB_FUNCTION("SSCaczu2aMQ", "libSceFont", 1, "libSceFont", sceFontDestroyString); + LIB_FUNCTION("hWE4AwNixqY", "libSceFont", 1, "libSceFont", sceFontDestroyWords); + LIB_FUNCTION("PEjv7CVDRYs", "libSceFont", 1, "libSceFont", sceFontDestroyWritingLine); + LIB_FUNCTION("UuY-OJF+f0k", "libSceFont", 1, "libSceFont", sceFontDettachDeviceCacheBuffer); + LIB_FUNCTION("C-4Qw5Srlyw", "libSceFont", 1, "libSceFont", sceFontGenerateCharGlyph); + LIB_FUNCTION("5kx49CAlO-M", "libSceFont", 1, "libSceFont", sceFontGetAttribute); + LIB_FUNCTION("OINC0X9HGBY", "libSceFont", 1, "libSceFont", sceFontGetCharGlyphCode); + LIB_FUNCTION("L97d+3OgMlE", "libSceFont", 1, "libSceFont", sceFontGetCharGlyphMetrics); + LIB_FUNCTION("ynSqYL8VpoA", "libSceFont", 1, "libSceFont", sceFontGetEffectSlant); + LIB_FUNCTION("d7dDgRY+Bzw", "libSceFont", 1, "libSceFont", sceFontGetEffectWeight); + LIB_FUNCTION("ZB8xRemRRG8", "libSceFont", 1, "libSceFont", sceFontGetFontGlyphsCount); + LIB_FUNCTION("4X14YSK4Ldk", "libSceFont", 1, "libSceFont", sceFontGetFontGlyphsOutlineProfile); + LIB_FUNCTION("eb9S3zNlV5o", "libSceFont", 1, "libSceFont", sceFontGetFontMetrics); + LIB_FUNCTION("tiIlroGki+g", "libSceFont", 1, "libSceFont", sceFontGetFontResolution); + LIB_FUNCTION("3hVv3SNoL6E", "libSceFont", 1, "libSceFont", sceFontGetFontStyleInformation); + LIB_FUNCTION("gVQpMBuB7fE", "libSceFont", 1, "libSceFont", sceFontGetGlyphExpandBufferState); + LIB_FUNCTION("imxVx8lm+KM", "libSceFont", 1, "libSceFont", sceFontGetHorizontalLayout); + LIB_FUNCTION("sDuhHGNhHvE", "libSceFont", 1, "libSceFont", sceFontGetKerning); + LIB_FUNCTION("LzmHDnlcwfQ", "libSceFont", 1, "libSceFont", sceFontGetLibrary); + LIB_FUNCTION("BozJej5T6fs", "libSceFont", 1, "libSceFont", sceFontGetPixelResolution); + LIB_FUNCTION("IQtleGLL5pQ", "libSceFont", 1, "libSceFont", sceFontGetRenderCharGlyphMetrics); + LIB_FUNCTION("Gqa5Pp7y4MU", "libSceFont", 1, "libSceFont", sceFontGetRenderEffectSlant); + LIB_FUNCTION("woOjHrkjIYg", "libSceFont", 1, "libSceFont", sceFontGetRenderEffectWeight); + LIB_FUNCTION("ryPlnDDI3rU", "libSceFont", 1, "libSceFont", sceFontGetRenderScaledKerning); + LIB_FUNCTION("EY38A01lq2k", "libSceFont", 1, "libSceFont", sceFontGetRenderScalePixel); + LIB_FUNCTION("FEafYUcxEGo", "libSceFont", 1, "libSceFont", sceFontGetRenderScalePoint); + LIB_FUNCTION("8REoLjNGCpM", "libSceFont", 1, "libSceFont", sceFontGetResolutionDpi); + LIB_FUNCTION("CkVmLoCNN-8", "libSceFont", 1, "libSceFont", sceFontGetScalePixel); + LIB_FUNCTION("GoF2bhB7LYk", "libSceFont", 1, "libSceFont", sceFontGetScalePoint); + LIB_FUNCTION("IrXeG0Lc6nA", "libSceFont", 1, "libSceFont", sceFontGetScriptLanguage); + LIB_FUNCTION("7-miUT6pNQw", "libSceFont", 1, "libSceFont", sceFontGetTypographicDesign); + LIB_FUNCTION("3BrWWFU+4ts", "libSceFont", 1, "libSceFont", sceFontGetVerticalLayout); + LIB_FUNCTION("8-zmgsxkBek", "libSceFont", 1, "libSceFont", sceFontGlyphDefineAttribute); + LIB_FUNCTION("oO33Uex4Ui0", "libSceFont", 1, "libSceFont", sceFontGlyphGetAttribute); + LIB_FUNCTION("PXlA0M8ax40", "libSceFont", 1, "libSceFont", sceFontGlyphGetGlyphForm); + LIB_FUNCTION("XUfSWpLhrUw", "libSceFont", 1, "libSceFont", sceFontGlyphGetMetricsForm); + LIB_FUNCTION("lNnUqa1zA-M", "libSceFont", 1, "libSceFont", sceFontGlyphGetScalePixel); + LIB_FUNCTION("ntrc3bEWlvQ", "libSceFont", 1, "libSceFont", sceFontGlyphRefersMetrics); + LIB_FUNCTION("9kTbF59TjLs", "libSceFont", 1, "libSceFont", sceFontGlyphRefersMetricsHorizontal); + LIB_FUNCTION("nJavPEdMDvM", "libSceFont", 1, "libSceFont", + sceFontGlyphRefersMetricsHorizontalAdvance); + LIB_FUNCTION("JCnVgZgcucs", "libSceFont", 1, "libSceFont", + sceFontGlyphRefersMetricsHorizontalX); + LIB_FUNCTION("R1T4i+DOhNY", "libSceFont", 1, "libSceFont", sceFontGlyphRefersOutline); + LIB_FUNCTION("RmkXfBcZnrM", "libSceFont", 1, "libSceFont", sceFontGlyphRenderImage); + LIB_FUNCTION("r4KEihtwxGs", "libSceFont", 1, "libSceFont", sceFontGlyphRenderImageHorizontal); + LIB_FUNCTION("n22d-HIdmMg", "libSceFont", 1, "libSceFont", sceFontGlyphRenderImageVertical); + LIB_FUNCTION("RL2cAQgyXR8", "libSceFont", 1, "libSceFont", sceFontGraphicsBeginFrame); + LIB_FUNCTION("dUmIK6QjT7E", "libSceFont", 1, "libSceFont", sceFontGraphicsDrawingCancel); + LIB_FUNCTION("X2Vl3yU19Zw", "libSceFont", 1, "libSceFont", sceFontGraphicsDrawingFinish); + LIB_FUNCTION("DOmdOwV3Aqw", "libSceFont", 1, "libSceFont", sceFontGraphicsEndFrame); + LIB_FUNCTION("zdYdKRQC3rw", "libSceFont", 1, "libSceFont", sceFontGraphicsExchangeResource); + LIB_FUNCTION("UkMUIoj-e9s", "libSceFont", 1, "libSceFont", sceFontGraphicsFillMethodInit); + LIB_FUNCTION("DJURdcnVUqo", "libSceFont", 1, "libSceFont", sceFontGraphicsFillPlotInit); + LIB_FUNCTION("eQac6ftmBQQ", "libSceFont", 1, "libSceFont", sceFontGraphicsFillPlotSetLayout); + LIB_FUNCTION("PEYQJa+MWnk", "libSceFont", 1, "libSceFont", sceFontGraphicsFillPlotSetMapping); + LIB_FUNCTION("21g4m4kYF6g", "libSceFont", 1, "libSceFont", sceFontGraphicsFillRatesInit); + LIB_FUNCTION("pJzji5FvdxU", "libSceFont", 1, "libSceFont", + sceFontGraphicsFillRatesSetFillEffect); + LIB_FUNCTION("scaro-xEuUM", "libSceFont", 1, "libSceFont", sceFontGraphicsFillRatesSetLayout); + LIB_FUNCTION("W66Kqtt0xU0", "libSceFont", 1, "libSceFont", sceFontGraphicsFillRatesSetMapping); + LIB_FUNCTION("FzpLsBQEegQ", "libSceFont", 1, "libSceFont", sceFontGraphicsGetDeviceUsage); + LIB_FUNCTION("W80hs0g5d+E", "libSceFont", 1, "libSceFont", sceFontGraphicsRegionInit); + LIB_FUNCTION("S48+njg9p-o", "libSceFont", 1, "libSceFont", sceFontGraphicsRegionInitCircular); + LIB_FUNCTION("wcOQ8Fz73+M", "libSceFont", 1, "libSceFont", sceFontGraphicsRegionInitRoundish); + LIB_FUNCTION("YBaw2Yyfd5E", "libSceFont", 1, "libSceFont", sceFontGraphicsRelease); + LIB_FUNCTION("qkySrQ4FGe0", "libSceFont", 1, "libSceFont", sceFontGraphicsRenderResource); + LIB_FUNCTION("qzNjJYKVli0", "libSceFont", 1, "libSceFont", sceFontGraphicsSetFramePolicy); + LIB_FUNCTION("9iRbHCtcx-o", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupClipping); + LIB_FUNCTION("KZ3qPyz5Opc", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupColorRates); + LIB_FUNCTION("LqclbpVzRvM", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupFillMethod); + LIB_FUNCTION("Wl4FiI4qKY0", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupFillRates); + LIB_FUNCTION("WC7s95TccVo", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupGlyphFill); + LIB_FUNCTION("zC6I4ty37NA", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupGlyphFillPlot); + LIB_FUNCTION("drZUF0XKTEI", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupHandleDefault); + LIB_FUNCTION("MEAmHMynQXE", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupLocation); + LIB_FUNCTION("XRUOmQhnYO4", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupPositioning); + LIB_FUNCTION("98XGr2Bkklg", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupRotation); + LIB_FUNCTION("Nj-ZUVOVAvc", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupScaling); + LIB_FUNCTION("p0avT2ggev0", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupShapeFill); + LIB_FUNCTION("0C5aKg9KghY", "libSceFont", 1, "libSceFont", sceFontGraphicsSetupShapeFillPlot); + LIB_FUNCTION("4pA3qqAcYco", "libSceFont", 1, "libSceFont", sceFontGraphicsStructureCanvas); + LIB_FUNCTION("cpjgdlMYdOM", "libSceFont", 1, "libSceFont", + sceFontGraphicsStructureCanvasSequence); + LIB_FUNCTION("774Mee21wKk", "libSceFont", 1, "libSceFont", sceFontGraphicsStructureDesign); + LIB_FUNCTION("Hp3NIFhUXvQ", "libSceFont", 1, "libSceFont", + sceFontGraphicsStructureDesignResource); + LIB_FUNCTION("bhmZlml6NBs", "libSceFont", 1, "libSceFont", + sceFontGraphicsStructureSurfaceTexture); + LIB_FUNCTION("5sAWgysOBfE", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateClipping); + LIB_FUNCTION("W4e8obm+w6o", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateColorRates); + LIB_FUNCTION("EgIn3QBajPs", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateFillMethod); + LIB_FUNCTION("MnUYAs2jVuU", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateFillRates); + LIB_FUNCTION("R-oVDMusYbc", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateGlyphFill); + LIB_FUNCTION("b9R+HQuHSMI", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateGlyphFillPlot); + LIB_FUNCTION("IN4P5pJADQY", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateLocation); + LIB_FUNCTION("U+LLXdr2DxM", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdatePositioning); + LIB_FUNCTION("yStTYSeb4NM", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateRotation); + LIB_FUNCTION("eDxmMoxE5xU", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateScaling); + LIB_FUNCTION("Ax6LQJJq6HQ", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateShapeFill); + LIB_FUNCTION("I5Rf2rXvBKQ", "libSceFont", 1, "libSceFont", sceFontGraphicsUpdateShapeFillPlot); + LIB_FUNCTION("whrS4oksXc4", "libSceFont", 1, "libSceFont", sceFontMemoryInit); + LIB_FUNCTION("h6hIgxXEiEc", "libSceFont", 1, "libSceFont", sceFontMemoryTerm); + LIB_FUNCTION("RvXyHMUiLhE", "libSceFont", 1, "libSceFont", sceFontOpenFontFile); + LIB_FUNCTION("JzCH3SCFnAU", "libSceFont", 1, "libSceFont", sceFontOpenFontInstance); + LIB_FUNCTION("KXUpebrFk1U", "libSceFont", 1, "libSceFont", sceFontOpenFontMemory); + LIB_FUNCTION("cKYtVmeSTcw", "libSceFont", 1, "libSceFont", sceFontOpenFontSet); + LIB_FUNCTION("Z2cdsqJH+5k", "libSceFont", 1, "libSceFont", sceFontRebindRenderer); + LIB_FUNCTION("3G4zhgKuxE8", "libSceFont", 1, "libSceFont", sceFontRenderCharGlyphImage); + LIB_FUNCTION("kAenWy1Zw5o", "libSceFont", 1, "libSceFont", + sceFontRenderCharGlyphImageHorizontal); + LIB_FUNCTION("i6UNdSig1uE", "libSceFont", 1, "libSceFont", sceFontRenderCharGlyphImageVertical); + LIB_FUNCTION("amcmrY62BD4", "libSceFont", 1, "libSceFont", sceFontRendererGetOutlineBufferSize); + LIB_FUNCTION("ai6AfGrBs4o", "libSceFont", 1, "libSceFont", sceFontRendererResetOutlineBuffer); + LIB_FUNCTION("ydF+WuH0fAk", "libSceFont", 1, "libSceFont", + sceFontRendererSetOutlineBufferPolicy); + LIB_FUNCTION("gdUCnU0gHdI", "libSceFont", 1, "libSceFont", sceFontRenderSurfaceInit); + LIB_FUNCTION("vRxf4d0ulPs", "libSceFont", 1, "libSceFont", sceFontRenderSurfaceSetScissor); + LIB_FUNCTION("0hr-w30SjiI", "libSceFont", 1, "libSceFont", sceFontRenderSurfaceSetStyleFrame); + LIB_FUNCTION("TMtqoFQjjbA", "libSceFont", 1, "libSceFont", sceFontSetEffectSlant); + LIB_FUNCTION("v0phZwa4R5o", "libSceFont", 1, "libSceFont", sceFontSetEffectWeight); + LIB_FUNCTION("kihFGYJee7o", "libSceFont", 1, "libSceFont", sceFontSetFontsOpenMode); + LIB_FUNCTION("I1acwR7Qp8E", "libSceFont", 1, "libSceFont", sceFontSetResolutionDpi); + LIB_FUNCTION("N1EBMeGhf7E", "libSceFont", 1, "libSceFont", sceFontSetScalePixel); + LIB_FUNCTION("sw65+7wXCKE", "libSceFont", 1, "libSceFont", sceFontSetScalePoint); + LIB_FUNCTION("PxSR9UfJ+SQ", "libSceFont", 1, "libSceFont", sceFontSetScriptLanguage); + LIB_FUNCTION("SnsZua35ngs", "libSceFont", 1, "libSceFont", sceFontSetTypographicDesign); + LIB_FUNCTION("lz9y9UFO2UU", "libSceFont", 1, "libSceFont", sceFontSetupRenderEffectSlant); + LIB_FUNCTION("XIGorvLusDQ", "libSceFont", 1, "libSceFont", sceFontSetupRenderEffectWeight); + LIB_FUNCTION("6vGCkkQJOcI", "libSceFont", 1, "libSceFont", sceFontSetupRenderScalePixel); + LIB_FUNCTION("nMZid4oDfi4", "libSceFont", 1, "libSceFont", sceFontSetupRenderScalePoint); + LIB_FUNCTION("ObkDGDBsVtw", "libSceFont", 1, "libSceFont", sceFontStringGetTerminateCode); + LIB_FUNCTION("+B-xlbiWDJ4", "libSceFont", 1, "libSceFont", sceFontStringGetTerminateOrder); + LIB_FUNCTION("o1vIEHeb6tw", "libSceFont", 1, "libSceFont", sceFontStringGetWritingForm); + LIB_FUNCTION("hq5LffQjz-s", "libSceFont", 1, "libSceFont", sceFontStringRefersRenderCharacters); + LIB_FUNCTION("Avv7OApgCJk", "libSceFont", 1, "libSceFont", sceFontStringRefersTextCharacters); + LIB_FUNCTION("lOfduYnjgbo", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetEffectSlant); + LIB_FUNCTION("HIUdjR-+Wl8", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetEffectWeight); + LIB_FUNCTION("VSw18Aqzl0U", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetResolutionDpi); + LIB_FUNCTION("2QfqfeLblbg", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetScalePixel); + LIB_FUNCTION("7x2xKiiB7MA", "libSceFont", 1, "libSceFont", sceFontStyleFrameGetScalePoint); + LIB_FUNCTION("la2AOWnHEAc", "libSceFont", 1, "libSceFont", sceFontStyleFrameInit); + LIB_FUNCTION("394sckksiCU", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetEffectSlant); + LIB_FUNCTION("faw77-pEBmU", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetEffectWeight); + LIB_FUNCTION("dB4-3Wdwls8", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetResolutionDpi); + LIB_FUNCTION("da4rQ4-+p-4", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetScalePixel); + LIB_FUNCTION("O997laxY-Ys", "libSceFont", 1, "libSceFont", sceFontStyleFrameSetScalePoint); + LIB_FUNCTION("dUmABkAnVgk", "libSceFont", 1, "libSceFont", sceFontStyleFrameUnsetEffectSlant); + LIB_FUNCTION("hwsuXgmKdaw", "libSceFont", 1, "libSceFont", sceFontStyleFrameUnsetEffectWeight); + LIB_FUNCTION("bePC0L0vQWY", "libSceFont", 1, "libSceFont", sceFontStyleFrameUnsetScale); + LIB_FUNCTION("mz2iTY0MK4A", "libSceFont", 1, "libSceFont", sceFontSupportExternalFonts); + LIB_FUNCTION("71w5DzObuZI", "libSceFont", 1, "libSceFont", sceFontSupportGlyphs); + LIB_FUNCTION("SsRbbCiWoGw", "libSceFont", 1, "libSceFont", sceFontSupportSystemFonts); + LIB_FUNCTION("IPoYwwlMx-g", "libSceFont", 1, "libSceFont", sceFontTextCodesStepBack); + LIB_FUNCTION("olSmXY+XP1E", "libSceFont", 1, "libSceFont", sceFontTextCodesStepNext); + LIB_FUNCTION("oaJ1BpN2FQk", "libSceFont", 1, "libSceFont", sceFontTextSourceInit); + LIB_FUNCTION("VRFd3diReec", "libSceFont", 1, "libSceFont", sceFontTextSourceRewind); + LIB_FUNCTION("eCRMCSk96NU", "libSceFont", 1, "libSceFont", sceFontTextSourceSetDefaultFont); + LIB_FUNCTION("OqQKX0h5COw", "libSceFont", 1, "libSceFont", sceFontTextSourceSetWritingForm); + LIB_FUNCTION("1QjhKxrsOB8", "libSceFont", 1, "libSceFont", sceFontUnbindRenderer); + LIB_FUNCTION("H-FNq8isKE0", "libSceFont", 1, "libSceFont", sceFontWordsFindWordCharacters); + LIB_FUNCTION("fljdejMcG1c", "libSceFont", 1, "libSceFont", sceFontWritingGetRenderMetrics); + LIB_FUNCTION("fD5rqhEXKYQ", "libSceFont", 1, "libSceFont", sceFontWritingInit); + LIB_FUNCTION("1+DgKL0haWQ", "libSceFont", 1, "libSceFont", sceFontWritingLineClear); + LIB_FUNCTION("JQKWIsS9joE", "libSceFont", 1, "libSceFont", sceFontWritingLineGetOrderingSpace); + LIB_FUNCTION("nlU2VnfpqTM", "libSceFont", 1, "libSceFont", sceFontWritingLineGetRenderMetrics); + LIB_FUNCTION("+FYcYefsVX0", "libSceFont", 1, "libSceFont", sceFontWritingLineRefersRenderStep); + LIB_FUNCTION("wyKFUOWdu3Q", "libSceFont", 1, "libSceFont", sceFontWritingLineWritesOrder); + LIB_FUNCTION("W-2WOXEHGck", "libSceFont", 1, "libSceFont", sceFontWritingRefersRenderStep); + LIB_FUNCTION("f4Onl7efPEY", "libSceFont", 1, "libSceFont", + sceFontWritingRefersRenderStepCharacter); + LIB_FUNCTION("BbCZjJizU4A", "libSceFont", 1, "libSceFont", sceFontWritingSetMaskInvisible); + LIB_FUNCTION("APTXePHIjLM", "libSceFont", 1, "libSceFont", Func_00F4D778F1C88CB3); + LIB_FUNCTION("A8ZQAl+7Dec", "libSceFont", 1, "libSceFont", Func_03C650025FBB0DE7); + LIB_FUNCTION("B+q4oWOyfho", "libSceFont", 1, "libSceFont", Func_07EAB8A163B27E1A); + LIB_FUNCTION("CUCOiOT5fOM", "libSceFont", 1, "libSceFont", Func_09408E88E4F97CE3); + LIB_FUNCTION("CfkpBe2CqBQ", "libSceFont", 1, "libSceFont", Func_09F92905ED82A814); + LIB_FUNCTION("DRQs7hqyGr4", "libSceFont", 1, "libSceFont", Func_0D142CEE1AB21ABE); + LIB_FUNCTION("FL0unhGcFvI", "libSceFont", 1, "libSceFont", Func_14BD2E9E119C16F2); + LIB_FUNCTION("GsU8nt6ujXU", "libSceFont", 1, "libSceFont", Func_1AC53C9EDEAE8D75); + LIB_FUNCTION("HUARhdXiTD0", "libSceFont", 1, "libSceFont", Func_1D401185D5E24C3D); + LIB_FUNCTION("HoPNIMLMmW8", "libSceFont", 1, "libSceFont", Func_1E83CD20C2CC996F); + LIB_FUNCTION("MUsfdluf54o", "libSceFont", 1, "libSceFont", Func_314B1F765B9FE78A); + LIB_FUNCTION("NQ5nJf7eKeE", "libSceFont", 1, "libSceFont", Func_350E6725FEDE29E1); + LIB_FUNCTION("Pbdz8KYEvzk", "libSceFont", 1, "libSceFont", Func_3DB773F0A604BF39); + LIB_FUNCTION("T-Sd0h4xGxw", "libSceFont", 1, "libSceFont", Func_4FF49DD21E311B1C); + LIB_FUNCTION("UmKHZkpJOYE", "libSceFont", 1, "libSceFont", Func_526287664A493981); + LIB_FUNCTION("VcpxjbyEpuk", "libSceFont", 1, "libSceFont", Func_55CA718DBC84A6E9); + LIB_FUNCTION("Vj-F8HBqi00", "libSceFont", 1, "libSceFont", Func_563FC5F0706A8B4D); + LIB_FUNCTION("Vp4uzTQpD0U", "libSceFont", 1, "libSceFont", Func_569E2ECD34290F45); + LIB_FUNCTION("WgR3W2vkdoU", "libSceFont", 1, "libSceFont", Func_5A04775B6BE47685); + LIB_FUNCTION("X9k7yrb3l1A", "libSceFont", 1, "libSceFont", Func_5FD93BCAB6F79750); + LIB_FUNCTION("YrU5j4ZL07Q", "libSceFont", 1, "libSceFont", Func_62B5398F864BD3B4); + LIB_FUNCTION("b5AQKU2CI2c", "libSceFont", 1, "libSceFont", Func_6F9010294D822367); + LIB_FUNCTION("d1fpR0I6emc", "libSceFont", 1, "libSceFont", Func_7757E947423A7A67); + LIB_FUNCTION("fga6Ugd-VPo", "libSceFont", 1, "libSceFont", Func_7E06BA52077F54FA); + LIB_FUNCTION("k7Nt6gITEdY", "libSceFont", 1, "libSceFont", Func_93B36DEA021311D6); + LIB_FUNCTION("lLCJHnERWYo", "libSceFont", 1, "libSceFont", Func_94B0891E7111598A); + LIB_FUNCTION("l4XJEowv580", "libSceFont", 1, "libSceFont", Func_9785C9128C2FE7CD); + LIB_FUNCTION("l9+8m2X7wOE", "libSceFont", 1, "libSceFont", Func_97DFBC9B65FBC0E1); + LIB_FUNCTION("rNlxdAXX08o", "libSceFont", 1, "libSceFont", Func_ACD9717405D7D3CA); + LIB_FUNCTION("sZqK7D-U8W8", "libSceFont", 1, "libSceFont", Func_B19A8AEC3FD4F16F); + LIB_FUNCTION("wQ9IitfPED0", "libSceFont", 1, "libSceFont", Func_C10F488AD7CF103D); + LIB_FUNCTION("0Mi1-0poJsc", "libSceFont", 1, "libSceFont", Func_D0C8B5FF4A6826C7); + LIB_FUNCTION("5I080Bw0KjM", "libSceFont", 1, "libSceFont", Func_E48D3CD01C342A33); + LIB_FUNCTION("6slrIYa3HhQ", "libSceFont", 1, "libSceFont", Func_EAC96B2186B71E14); + LIB_FUNCTION("-keIqW70YlY", "libSceFont", 1, "libSceFont", Func_FE4788A96EF46256); + LIB_FUNCTION("-n5a6V0wWPU", "libSceFont", 1, "libSceFont", Func_FE7E5AE95D3058F5); +}; + +} // namespace Libraries::Font \ No newline at end of file diff --git a/src/core/libraries/font/font.h b/src/core/libraries/font/font.h new file mode 100644 index 000000000..a8e239126 --- /dev/null +++ b/src/core/libraries/font/font.h @@ -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 \ No newline at end of file diff --git a/src/core/libraries/font/font_error.h b/src/core/libraries/font/font_error.h new file mode 100644 index 000000000..64e8fc6a7 --- /dev/null +++ b/src/core/libraries/font/font_error.h @@ -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; diff --git a/src/core/libraries/font/fontft.cpp b/src/core/libraries/font/fontft.cpp new file mode 100644 index 000000000..4a1dbb989 --- /dev/null +++ b/src/core/libraries/font/fontft.cpp @@ -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 \ No newline at end of file diff --git a/src/core/libraries/font/fontft.h b/src/core/libraries/font/fontft.h new file mode 100644 index 000000000..cec6d7872 --- /dev/null +++ b/src/core/libraries/font/fontft.h @@ -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 \ No newline at end of file diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 2c4b4a670..7ded1f33e 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -140,7 +140,7 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { return -1; } // Create a file if it doesn't exist - Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Write); + Common::FS::IOFile out(file->m_host_name, Common::FS::FileAccessMode::Create); } } else if (!exists) { // If we're not creating a file, and it doesn't exist, return ENOENT @@ -205,22 +205,30 @@ s32 PS4_SYSV_ABI open(const char* raw_path, s32 flags, u16 mode) { } if (read) { - // Read only + // Open exclusively for reading e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Read); } else if (read_only) { // Can't open files with write/read-write access in a read only directory h->DeleteHandle(handle); *__Error() = POSIX_EROFS; return -1; - } else if (append) { - // Append can be specified with rdwr or write, but we treat it as a separate mode. - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); } else if (write) { - // Write only - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); + if (append) { + // Open exclusively for appending + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Append); + } else { + // Open exclusively for writing + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::Write); + } } else if (rdwr) { // Read and write - e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); + if (append) { + // Open for reading and appending + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadAppend); + } else { + // Open for reading and writing + e = file->f.Open(file->m_host_name, Common::FS::FileAccessMode::ReadWrite); + } } } @@ -354,6 +362,12 @@ s64 PS4_SYSV_ABI readv(s32 fd, const OrbisKernelIovec* iov, s32 iovcnt) { } return result; } + + if (file->f.IsWriteOnly()) { + *__Error() = POSIX_EBADF; + return -1; + } + s64 total_read = 0; for (s32 i = 0; i < iovcnt; i++) { total_read += ReadFile(file->f, iov[i].iov_base, iov[i].iov_len); @@ -509,6 +523,12 @@ s64 PS4_SYSV_ABI read(s32 fd, void* buf, u64 nbytes) { // Socket functions handle errnos internally. return file->socket->ReceivePacket(buf, nbytes, 0, nullptr, 0); } + + if (file->f.IsWriteOnly()) { + *__Error() = POSIX_EBADF; + return -1; + } + return ReadFile(file->f, buf, nbytes); } @@ -620,17 +640,29 @@ s32 PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { *__Error() = POSIX_ENOENT; return -1; } + + // get the difference between file clock and system clock + const auto now_sys = std::chrono::system_clock::now(); + const auto now_file = std::filesystem::file_time_type::clock::now(); + // calculate the file modified time + const auto mtime = std::filesystem::last_write_time(path_name); + const auto mtimestamp = now_sys + (mtime - now_file); + if (std::filesystem::is_directory(path_name)) { sb->st_mode = 0000777u | 0040000u; sb->st_size = 65536; sb->st_blksize = 65536; sb->st_blocks = 128; + sb->st_mtim.tv_sec = + std::chrono::duration_cast(mtimestamp.time_since_epoch()).count(); // TODO incomplete } else { sb->st_mode = 0000777u | 0100000u; sb->st_size = static_cast(std::filesystem::file_size(path_name)); sb->st_blksize = 512; sb->st_blocks = (sb->st_size + 511) / 512; + sb->st_mtim.tv_sec = + std::chrono::duration_cast(mtimestamp.time_since_epoch()).count(); // TODO incomplete } @@ -801,11 +833,7 @@ s32 PS4_SYSV_ABI posix_rename(const char* from, const char* to) { auto* h = Common::Singleton::Instance(); auto file = h->GetFile(src_path); if (file) { - // We need to force ReadWrite if the file had Write access before - // Otherwise f.Open will clear the file contents. - auto access_mode = file->f.GetAccessMode() == Common::FS::FileAccessMode::Write - ? Common::FS::FileAccessMode::ReadWrite - : file->f.GetAccessMode(); + auto access_mode = file->f.GetAccessMode(); file->f.Close(); std::filesystem::remove(src_path); file->f.Open(dst_path, access_mode); @@ -855,6 +883,11 @@ s64 PS4_SYSV_ABI posix_preadv(s32 fd, OrbisKernelIovec* iov, s32 iovcnt, s64 off return result; } + if (file->f.IsWriteOnly()) { + *__Error() = POSIX_EBADF; + return -1; + } + const s64 pos = file->f.Tell(); SCOPE_EXIT { file->f.Seek(pos); diff --git a/src/core/libraries/kernel/kernel.cpp b/src/core/libraries/kernel/kernel.cpp index 054f34c15..6594bfab2 100644 --- a/src/core/libraries/kernel/kernel.cpp +++ b/src/core/libraries/kernel/kernel.cpp @@ -236,15 +236,24 @@ s32 PS4_SYSV_ABI sceKernelSetGPO() { return ORBIS_OK; } +s32 PS4_SYSV_ABI sceKernelGetAllowedSdkVersionOnSystem(s32* ver) { + if (ver == nullptr) { + return ORBIS_KERNEL_ERROR_EINVAL; + } + // Returns the highest game SDK version this PS4 allows. + *ver = CURRENT_FIRMWARE_VERSION | 0xfff; + LOG_INFO(Lib_Kernel, "called, returned sw version: {}", *ver); + return ORBIS_OK; +} + s32 PS4_SYSV_ABI sceKernelGetSystemSwVersion(SwVersionStruct* ret) { if (ret == nullptr) { - return ORBIS_OK; // but why? + return ORBIS_OK; } - ASSERT(ret->struct_size == 40); - u32 fake_fw = Common::ElfInfo::Instance().RawFirmwareVer(); + u32 fake_fw = CURRENT_FIRMWARE_VERSION; ret->hex_representation = fake_fw; std::snprintf(ret->text_representation, 28, "%2x.%03x.%03x", fake_fw >> 0x18, - fake_fw >> 0xc & 0xfff, fake_fw & 0xfff); // why %2x? + fake_fw >> 0xc & 0xfff, fake_fw & 0xfff); LOG_INFO(Lib_Kernel, "called, returned sw version: {}", ret->text_representation); return ORBIS_OK; } @@ -257,9 +266,13 @@ const char** PS4_SYSV_ABI getargv() { return entry_params.argv; } -s32 PS4_SYSV_ABI get_authinfo(int pid, AuthInfoData* p2) { +s32 PS4_SYSV_ABI get_authinfo(s32 pid, AuthInfoData* p2) { LOG_WARNING(Lib_Kernel, "(STUBBED) called, pid: {}", pid); - if ((pid != 0) && (pid != GLOBAL_PID)) { + if (p2 == nullptr) { + *Kernel::__Error() = POSIX_EPERM; + return -1; + } + if (pid != 0 && pid != GLOBAL_PID) { *Kernel::__Error() = POSIX_ESRCH; return -1; } @@ -269,6 +282,22 @@ s32 PS4_SYSV_ABI get_authinfo(int pid, AuthInfoData* p2) { return ORBIS_OK; } +s32 PS4_SYSV_ABI sceKernelGetAppInfo(s32 pid, OrbisKernelAppInfo* app_info) { + LOG_WARNING(Lib_Kernel, "(STUBBED) called, pid: {}", pid); + if (pid != GLOBAL_PID) { + return ORBIS_KERNEL_ERROR_EPERM; + } + if (app_info == nullptr) { + return ORBIS_OK; + } + + auto& game_info = Common::ElfInfo::Instance(); + *app_info = {}; + app_info->has_param_sfo = 1; + strncpy(app_info->cusa_name, game_info.GameSerial().data(), 10); + return ORBIS_OK; +} + void RegisterLib(Core::Loader::SymbolsResolver* sym) { service_thread = std::jthread{KernelServiceThread}; @@ -285,8 +314,10 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) { LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", &g_stack_chk_guard); LIB_FUNCTION("D4yla3vx4tY", "libkernel", 1, "libkernel", sceKernelError); + LIB_FUNCTION("YeU23Szo3BM", "libkernel", 1, "libkernel", sceKernelGetAllowedSdkVersionOnSystem); LIB_FUNCTION("Mv1zUObHvXI", "libkernel", 1, "libkernel", sceKernelGetSystemSwVersion); LIB_FUNCTION("igMefp4SAv0", "libkernel", 1, "libkernel", get_authinfo); + LIB_FUNCTION("G-MYv5erXaU", "libkernel", 1, "libkernel", sceKernelGetAppInfo); LIB_FUNCTION("PfccT7qURYE", "libkernel", 1, "libkernel", kernel_ioctl); LIB_FUNCTION("wW+k21cmbwQ", "libkernel", 1, "libkernel", kernel_ioctl); LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", sceKernelGetFsSandboxRandomWord); diff --git a/src/core/libraries/kernel/kernel.h b/src/core/libraries/kernel/kernel.h index 74c457dc5..91fac4c71 100644 --- a/src/core/libraries/kernel/kernel.h +++ b/src/core/libraries/kernel/kernel.h @@ -36,6 +36,8 @@ struct OrbisWrapperImpl { #define ORBIS(func) (Libraries::Kernel::OrbisWrapperImpl::wrap) +#define CURRENT_FIRMWARE_VERSION 0x13020011 + s32* PS4_SYSV_ABI __Error(); struct SwVersionStruct { @@ -51,6 +53,30 @@ struct AuthInfoData { u64 ucred[8]; }; +struct OrbisKernelTitleWorkaround { + s32 version; + s32 align; + u64 ids[2]; +}; + +struct OrbisKernelAppInfo { + s32 app_id; + s32 mmap_flags; + s32 attribute_exe; + s32 attribute2; + char cusa_name[10]; + u8 debug_level; + u8 slv_flags; + u8 mini_app_dmem_flags; + u8 render_mode; + u8 mdbg_out; + u8 required_hdcp_type; + u64 preload_prx_flags; + s32 attribute1; + s32 has_param_sfo; + OrbisKernelTitleWorkaround title_workaround; +}; + void RegisterLib(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/process.cpp b/src/core/libraries/kernel/process.cpp index 0e168e43a..02da041c3 100644 --- a/src/core/libraries/kernel/process.cpp +++ b/src/core/libraries/kernel/process.cpp @@ -21,8 +21,22 @@ s32 PS4_SYSV_ABI sceKernelIsNeoMode() { Common::ElfInfo::Instance().GetPSFAttributes().support_neo_mode; } +s32 PS4_SYSV_ABI sceKernelHasNeoMode() { + return Config::isNeoModeConsole(); +} + +s32 PS4_SYSV_ABI sceKernelGetMainSocId() { + // These hardcoded values are based on hardware observations. + // Different models of PS4/PS4 Pro likely return slightly different values. + LOG_DEBUG(Lib_Kernel, "called"); + if (Config::isNeoModeConsole()) { + return 0x740f30; + } + return 0x710f10; +} + s32 PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(s32* ver) { - s32 version = Common::ElfInfo::Instance().RawFirmwareVer(); + s32 version = Common::ElfInfo::Instance().CompiledSdkVer(); *ver = version; return (version >= 0) ? ORBIS_OK : ORBIS_KERNEL_ERROR_EINVAL; } @@ -31,6 +45,11 @@ s32 PS4_SYSV_ABI sceKernelGetCpumode() { return 0; } +s32 PS4_SYSV_ABI sceKernelGetCurrentCpu() { + LOG_DEBUG(Lib_Kernel, "called"); + return 0; +} + void* PS4_SYSV_ABI sceKernelGetProcParam() { auto* linker = Common::Singleton::Instance(); return linker->GetProcParam(); @@ -208,7 +227,10 @@ void RegisterProcess(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("xeu-pV8wkKs", "libkernel", 1, "libkernel", sceKernelIsInSandbox); LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", sceKernelGetCompiledSdkVersion); LIB_FUNCTION("WslcK1FQcGI", "libkernel", 1, "libkernel", sceKernelIsNeoMode); + LIB_FUNCTION("rNRtm1uioyY", "libkernel", 1, "libkernel", sceKernelHasNeoMode); + LIB_FUNCTION("0vTn5IDMU9A", "libkernel", 1, "libkernel", sceKernelGetMainSocId); LIB_FUNCTION("VOx8NGmHXTs", "libkernel", 1, "libkernel", sceKernelGetCpumode); + LIB_FUNCTION("g0VTBxfJyu0", "libkernel", 1, "libkernel", sceKernelGetCurrentCpu); LIB_FUNCTION("959qrazPIrg", "libkernel", 1, "libkernel", sceKernelGetProcParam); LIB_FUNCTION("wzvqT4UqKX8", "libkernel", 1, "libkernel", sceKernelLoadStartModule); LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", sceKernelDlsym); diff --git a/src/core/libraries/kernel/threads/condvar.cpp b/src/core/libraries/kernel/threads/condvar.cpp index 98f7397ad..9d429ed7d 100644 --- a/src/core/libraries/kernel/threads/condvar.cpp +++ b/src/core/libraries/kernel/threads/condvar.cpp @@ -354,6 +354,8 @@ void RegisterCond(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("Op8TBGY5KHg", "libkernel", 1, "libkernel", posix_pthread_cond_wait); LIB_FUNCTION("mkx2fVhNMsg", "libkernel", 1, "libkernel", posix_pthread_cond_broadcast); LIB_FUNCTION("2MOy+rUfuhQ", "libkernel", 1, "libkernel", posix_pthread_cond_signal); + LIB_FUNCTION("RXXqi4CtF8w", "libkernel", 1, "libkernel", posix_pthread_cond_destroy); + LIB_FUNCTION("27bAgiJmOh0", "libkernel", 1, "libkernel", posix_pthread_cond_timedwait); LIB_FUNCTION("mKoTx03HRWA", "libkernel", 1, "libkernel", posix_pthread_condattr_init); LIB_FUNCTION("dJcuQVn6-Iw", "libkernel", 1, "libkernel", posix_pthread_condattr_destroy); diff --git a/src/core/libraries/kernel/threads/mutex.cpp b/src/core/libraries/kernel/threads/mutex.cpp index 505bd0d9d..5d97c5dc1 100644 --- a/src/core/libraries/kernel/threads/mutex.cpp +++ b/src/core/libraries/kernel/threads/mutex.cpp @@ -442,6 +442,8 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("ltCfaGr2JGE", "libkernel", 1, "libkernel", posix_pthread_mutex_destroy); LIB_FUNCTION("dQHWEsJtoE4", "libkernel", 1, "libkernel", posix_pthread_mutexattr_init); LIB_FUNCTION("mDmgMOGVUqg", "libkernel", 1, "libkernel", posix_pthread_mutexattr_settype); + LIB_FUNCTION("HF7lK46xzjY", "libkernel", 1, "libkernel", posix_pthread_mutexattr_destroy); + LIB_FUNCTION("K-jXhbt2gn4", "libkernel", 1, "libkernel", posix_pthread_mutex_trylock); // Orbis LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", ORBIS(scePthreadMutexInit)); diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 09e8b9558..8ab8b72c3 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -663,6 +663,9 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("Z4QosVuAsA0", "libkernel", 1, "libkernel", posix_pthread_once); LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", posix_pthread_self); LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", posix_pthread_create); + LIB_FUNCTION("+U1R4WtXvoc", "libkernel", 1, "libkernel", posix_pthread_detach); + LIB_FUNCTION("7Xl257M4VNI", "libkernel", 1, "libkernel", posix_pthread_equal); + LIB_FUNCTION("h9CcP3J0oVM", "libkernel", 1, "libkernel", posix_pthread_join); LIB_FUNCTION("Jb2uGFMr688", "libkernel", 1, "libkernel", posix_pthread_getaffinity_np); LIB_FUNCTION("5KWrg7-ZqvE", "libkernel", 1, "libkernel", posix_pthread_setaffinity_np); LIB_FUNCTION("3eqs37G74-s", "libkernel", 1, "libkernel", posix_pthread_getthreadid_np); diff --git a/src/core/libraries/network/http.cpp b/src/core/libraries/network/http.cpp index 63e82ce81..1ae48dfed 100644 --- a/src/core/libraries/network/http.cpp +++ b/src/core/libraries/network/http.cpp @@ -9,6 +9,35 @@ namespace Libraries::Http { +static bool g_isHttpInitialized = true; // TODO temp always inited + +void NormalizeAndAppendPath(char* dest, char* src) { + char* lastSlash; + u64 length; + + lastSlash = strrchr(dest, '/'); + if (lastSlash == NULL) { + length = strlen(dest); + dest[length] = '/'; + dest[length + 1] = '\0'; + } else { + lastSlash[1] = '\0'; + } + if (*src == '/') { + dest[0] = '\0'; + } + length = strnlen(dest, 0x3fff); + strncat(dest, src, 0x3fff - length); + return; +} + +int HttpRequestInternal_Acquire(HttpRequestInternal** outRequest, u32 requestId) { + return 0; // TODO dummy +} +int HttpRequestInternal_Release(HttpRequestInternal* request) { + return 0; // TODO dummy +} + int PS4_SYSV_ABI sceHttpAbortRequest() { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; @@ -34,8 +63,9 @@ int PS4_SYSV_ABI sceHttpAddQuery() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpAddRequestHeader() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); +int PS4_SYSV_ABI sceHttpAddRequestHeader(int id, const char* name, const char* value, s32 mode) { + LOG_ERROR(Lib_Http, "(STUBBED) called id= {} name = {} value = {} mode = {}", id, + std::string(name), std::string(value), mode); return ORBIS_OK; } @@ -84,8 +114,9 @@ int PS4_SYSV_ABI sceHttpCreateConnection() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpCreateConnectionWithURL() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); +int PS4_SYSV_ABI sceHttpCreateConnectionWithURL(int tmplId, const char* url, bool enableKeepalive) { + LOG_ERROR(Lib_Http, "(STUBBED) called tmpid = {} url = {} enableKeepalive = {}", tmplId, + std::string(url), enableKeepalive ? 1 : 0); return ORBIS_OK; } @@ -104,8 +135,10 @@ int PS4_SYSV_ABI sceHttpCreateRequest2() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpCreateRequestWithURL() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); +int PS4_SYSV_ABI sceHttpCreateRequestWithURL(int connId, s32 method, const char* url, + u64 contentLength) { + LOG_ERROR(Lib_Http, "(STUBBED) called connId = {} method = {} url={} contentLength={}", connId, + method, url, contentLength); return ORBIS_OK; } @@ -184,7 +217,7 @@ int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpGetAllResponseHeaders() { +int PS4_SYSV_ABI sceHttpGetAllResponseHeaders(int reqId, char** header, u64* headerSize) { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_FAIL; } @@ -254,12 +287,42 @@ int PS4_SYSV_ABI sceHttpGetResponseContentLength() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpGetStatusCode() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); +int PS4_SYSV_ABI sceHttpGetStatusCode(int reqId, int* statusCode) { + LOG_ERROR(Lib_Http, "(STUBBED) called reqId = {}", reqId); +#if 0 + if (!g_isHttpInitialized) + return ORBIS_HTTP_ERROR_BEFORE_INIT; + + if (statusCode == nullptr) + return ORBIS_HTTP_ERROR_INVALID_VALUE; + + int ret = 0; + // Lookup HttpRequestInternal by reqId + HttpRequestInternal* request = nullptr; + ret = HttpRequestInternal_Acquire(&request, reqId); + if (ret < 0) + return ret; + request->m_mutex.lock(); + if (request->state > 0x11) { + if (request->state == 0x16) { + ret = request->errorCode; + } else { + *statusCode = request->httpStatusCode; + ret = 0; + } + } else { + ret = ORBIS_HTTP_ERROR_BEFORE_SEND; + } + request->m_mutex.unlock(); + HttpRequestInternal_Release(request); + + return ret; +#else return ORBIS_OK; +#endif } -int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, std::size_t poolSize) { +int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, u64 poolSize) { LOG_ERROR(Lib_Http, "(DUMMY) called libnetMemId = {} libsslCtxId = {} poolSize = {}", libnetMemId, libsslCtxId, poolSize); // return a value >1 @@ -267,14 +330,104 @@ int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, std::size_t poolS return ++id; } -int PS4_SYSV_ABI sceHttpParseResponseHeader() { +int PS4_SYSV_ABI sceHttpParseResponseHeader(const char* header, u64 headerLen, const char* fieldStr, + const char** fieldValue, u64* valueLen) { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpParseStatusLine() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceHttpParseStatusLine(const char* statusLine, u64 lineLen, int32_t* httpMajorVer, + int32_t* httpMinorVer, int32_t* responseCode, + const char** reasonPhrase, u64* phraseLen) { + if (!statusLine) { + LOG_ERROR(Lib_Http, "Invalid response"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + if (!httpMajorVer || !httpMinorVer || !responseCode || !reasonPhrase || !phraseLen) { + LOG_ERROR(Lib_Http, "Invalid value"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_VALUE; + } + *httpMajorVer = 0; + *httpMinorVer = 0; + if (lineLen < 8) { + LOG_ERROR(Lib_Http, "Linelen is smaller than 8"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + if (strncmp(statusLine, "HTTP/", 5) != 0) { + LOG_ERROR(Lib_Http, "statusLine doesn't start with HTTP/"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + + u64 index = 5; + + if (!isdigit(statusLine[index])) { + LOG_ERROR(Lib_Http, "Invalid response"); + + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + + while (isdigit(statusLine[index])) { + *httpMajorVer = *httpMajorVer * 10 + (statusLine[index] - '0'); + index++; + } + + if (statusLine[index] != '.') { + LOG_ERROR(Lib_Http, "Invalid response"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + index++; + + if (!isdigit(statusLine[index])) { + LOG_ERROR(Lib_Http, "Invalid response"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + + while (isdigit(statusLine[index])) { + *httpMinorVer = *httpMinorVer * 10 + (statusLine[index] - '0'); + index++; + } + + if (statusLine[index] != ' ') { + LOG_ERROR(Lib_Http, "Invalid response"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + index++; + + // Validate and parse the 3-digit HTTP response code + if (lineLen - index < 3 || !isdigit(statusLine[index]) || !isdigit(statusLine[index + 1]) || + !isdigit(statusLine[index + 2])) { + LOG_ERROR(Lib_Http, "Invalid response"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + + *responseCode = (statusLine[index] - '0') * 100 + (statusLine[index + 1] - '0') * 10 + + (statusLine[index + 2] - '0'); + index += 3; + + if (statusLine[index] != ' ') { + LOG_ERROR(Lib_Http, "Invalid response"); + return ORBIS_HTTP_ERROR_PARSE_HTTP_INVALID_RESPONSE; + } + index++; + + // Set the reason phrase start position + *reasonPhrase = &statusLine[index]; + u64 phraseStart = index; + + while (index < lineLen && statusLine[index] != '\n') { + index++; + } + + // Determine the length of the reason phrase, excluding trailing \r if present + if (index == phraseStart) { + *phraseLen = 0; + } else { + *phraseLen = + (statusLine[index - 1] == '\r') ? (index - phraseStart - 1) : (index - phraseStart); + } + + // Return the number of bytes processed + return index + 1; } int PS4_SYSV_ABI sceHttpReadData() { @@ -317,8 +470,8 @@ int PS4_SYSV_ABI sceHttpsEnableOptionPrivate() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpSendRequest() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); +int PS4_SYSV_ABI sceHttpSendRequest(int reqId, const void* postData, u64 size) { + LOG_ERROR(Lib_Http, "(STUBBED) called reqId = {} size = {}", reqId, size); return ORBIS_OK; } @@ -548,7 +701,8 @@ int PS4_SYSV_ABI sceHttpUnsetEpoll() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpUriBuild() { +int PS4_SYSV_ABI sceHttpUriBuild(char* out, u64* require, u64 prepare, + const OrbisHttpUriElement* srcElement, u32 option) { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; } @@ -563,13 +717,97 @@ int PS4_SYSV_ABI sceHttpUriEscape() { return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpUriMerge() { - LOG_ERROR(Lib_Http, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceHttpUriMerge(char* mergedUrl, char* url, char* relativeUri, u64* require, + u64 prepare, u32 option) { + u64 requiredLength; + int returnValue; + u64 baseUrlLength; + u64 relativeUriLength; + u64 totalLength; + u64 combinedLength; + int parseResult; + u64 localSizeRelativeUri; + u64 localSizeBaseUrl; + OrbisHttpUriElement parsedUriElement; + + if (option != 0 || url == NULL || relativeUri == NULL) { + LOG_ERROR(Lib_Http, "Invalid value"); + return ORBIS_HTTP_ERROR_INVALID_VALUE; + } + + returnValue = sceHttpUriParse(NULL, url, NULL, &localSizeBaseUrl, 0); + if (returnValue < 0) { + LOG_ERROR(Lib_Http, "returning {:#x}", returnValue); + return returnValue; + } + + returnValue = sceHttpUriParse(NULL, relativeUri, NULL, &localSizeRelativeUri, 0); + if (returnValue < 0) { + LOG_ERROR(Lib_Http, "returning {:#x}", returnValue); + return returnValue; + } + + baseUrlLength = strnlen(url, 0x3fff); + relativeUriLength = strnlen(relativeUri, 0x3fff); + requiredLength = localSizeBaseUrl + 2 + (relativeUriLength + baseUrlLength) * 2; + + if (require) { + *require = requiredLength; + } + + if (mergedUrl == NULL) { + return ORBIS_OK; + } + + if (prepare < requiredLength) { + LOG_ERROR(Lib_Http, "Error Out of memory"); + return ORBIS_HTTP_ERROR_OUT_OF_MEMORY; + } + + totalLength = strnlen(url, 0x3fff); + baseUrlLength = strnlen(relativeUri, 0x3fff); + combinedLength = totalLength + 1 + baseUrlLength; + relativeUriLength = prepare - combinedLength; + + returnValue = + sceHttpUriParse(&parsedUriElement, relativeUri, mergedUrl + totalLength + baseUrlLength + 1, + &localSizeRelativeUri, relativeUriLength); + if (returnValue < 0) { + LOG_ERROR(Lib_Http, "returning {:#x}", returnValue); + return returnValue; + } + if (parsedUriElement.scheme == NULL) { + strncpy(mergedUrl, relativeUri, requiredLength); + if (require) { + *require = strnlen(relativeUri, 0x3fff) + 1; + } + return ORBIS_OK; + } + + returnValue = + sceHttpUriParse(&parsedUriElement, url, mergedUrl + totalLength + baseUrlLength + 1, + &localSizeBaseUrl, relativeUriLength); + if (returnValue < 0) { + LOG_ERROR(Lib_Http, "returning {:#x}", returnValue); + return returnValue; + } + + combinedLength += localSizeBaseUrl; + strncpy(mergedUrl + combinedLength, parsedUriElement.path, prepare - combinedLength); + NormalizeAndAppendPath(mergedUrl + combinedLength, relativeUri); + + returnValue = sceHttpUriBuild(mergedUrl, 0, ~(baseUrlLength + totalLength) + prepare, + &parsedUriElement, 0x3f); + if (returnValue >= 0) { + return ORBIS_OK; + } else { + LOG_ERROR(Lib_Http, "returning {:#x}", returnValue); + return returnValue; + } } int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool, - size_t* require, size_t prepare) { + u64* require, u64 prepare) { LOG_INFO(Lib_Http, "srcUri = {}", std::string(srcUri)); if (!srcUri) { LOG_ERROR(Lib_Http, "invalid url"); @@ -586,10 +824,10 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v } // Track the total required buffer size - size_t requiredSize = 0; + u64 requiredSize = 0; // Parse the scheme (e.g., "http:", "https:", "file:") - size_t schemeLength = 0; + u64 schemeLength = 0; while (srcUri[schemeLength] && srcUri[schemeLength] != ':') { if (!isalnum(srcUri[schemeLength])) { LOG_ERROR(Lib_Http, "invalid url"); @@ -611,7 +849,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v requiredSize += schemeLength + 1; // Move past the scheme and ':' character - size_t offset = schemeLength + 1; + u64 offset = schemeLength + 1; // Check if "//" appears after the scheme if (strncmp(srcUri + offset, "//", 2) == 0) { @@ -638,7 +876,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v // Parse the path (everything after the slashes) char* pathStart = (char*)srcUri + offset; - size_t pathLength = 0; + u64 pathLength = 0; while (pathStart[pathLength] && pathStart[pathLength] != '?' && pathStart[pathLength] != '#') { pathLength++; @@ -689,7 +927,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v hostStart++; } - size_t hostLength = 0; + u64 hostLength = 0; while (hostStart[hostLength] && hostStart[hostLength] != '/' && hostStart[hostLength] != '?' && hostStart[hostLength] != ':') { hostLength++; @@ -714,7 +952,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v // Parse the port (if present) if (hostStart[hostLength] == ':') { char* portStart = hostStart + hostLength + 1; - size_t portLength = 0; + u64 portLength = 0; while (portStart[portLength] && isdigit(portStart[portLength])) { portLength++; } @@ -754,7 +992,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v // Parse the path (if present) if (srcUri[offset] == '/') { char* pathStart = (char*)srcUri + offset; - size_t pathLength = 0; + u64 pathLength = 0; while (pathStart[pathLength] && pathStart[pathLength] != '?' && pathStart[pathLength] != '#') { pathLength++; @@ -780,7 +1018,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v // Parse the query (if present) if (srcUri[offset] == '?') { char* queryStart = (char*)srcUri + offset + 1; - size_t queryLength = 0; + u64 queryLength = 0; while (queryStart[queryLength] && queryStart[queryLength] != '#') { queryLength++; } @@ -805,7 +1043,7 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v // Parse the fragment (if present) if (srcUri[offset] == '#') { char* fragmentStart = (char*)srcUri + offset + 1; - size_t fragmentLength = 0; + u64 fragmentLength = 0; while (fragmentStart[fragmentLength]) { fragmentLength++; } @@ -833,12 +1071,12 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, size_t srcSize) { +int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, u64 srcSize) { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; } -int PS4_SYSV_ABI sceHttpUriUnescape(char* out, size_t* require, size_t prepare, const char* in) { +int PS4_SYSV_ABI sceHttpUriUnescape(char* out, u64* require, u64 prepare, const char* in) { LOG_ERROR(Lib_Http, "(STUBBED) called"); return ORBIS_OK; } diff --git a/src/core/libraries/network/http.h b/src/core/libraries/network/http.h index 228080207..701bb0e05 100644 --- a/src/core/libraries/network/http.h +++ b/src/core/libraries/network/http.h @@ -3,6 +3,7 @@ #pragma once +#include #include "common/types.h" #include "core/libraries/network/ssl.h" @@ -25,6 +26,12 @@ struct OrbisHttpUriElement { u8 reserved[10]; }; +struct HttpRequestInternal { + int state; // +0x20 + int errorCode; // +0x28 + int httpStatusCode; // +0x20C + std::mutex m_mutex; +}; using OrbisHttpsCaList = Libraries::Ssl::OrbisSslCaList; int PS4_SYSV_ABI sceHttpAbortRequest(); @@ -32,7 +39,7 @@ int PS4_SYSV_ABI sceHttpAbortRequestForce(); int PS4_SYSV_ABI sceHttpAbortWaitRequest(); int PS4_SYSV_ABI sceHttpAddCookie(); int PS4_SYSV_ABI sceHttpAddQuery(); -int PS4_SYSV_ABI sceHttpAddRequestHeader(); +int PS4_SYSV_ABI sceHttpAddRequestHeader(int id, const char* name, const char* value, s32 mode); int PS4_SYSV_ABI sceHttpAddRequestHeaderRaw(); int PS4_SYSV_ABI sceHttpAuthCacheExport(); int PS4_SYSV_ABI sceHttpAuthCacheFlush(); @@ -42,11 +49,12 @@ int PS4_SYSV_ABI sceHttpCookieExport(); int PS4_SYSV_ABI sceHttpCookieFlush(); int PS4_SYSV_ABI sceHttpCookieImport(); int PS4_SYSV_ABI sceHttpCreateConnection(); -int PS4_SYSV_ABI sceHttpCreateConnectionWithURL(); +int PS4_SYSV_ABI sceHttpCreateConnectionWithURL(int tmplId, const char* url, bool enableKeepalive); int PS4_SYSV_ABI sceHttpCreateEpoll(); int PS4_SYSV_ABI sceHttpCreateRequest(); int PS4_SYSV_ABI sceHttpCreateRequest2(); -int PS4_SYSV_ABI sceHttpCreateRequestWithURL(); +int PS4_SYSV_ABI sceHttpCreateRequestWithURL(int connId, s32 method, const char* url, + u64 contentLength); int PS4_SYSV_ABI sceHttpCreateRequestWithURL2(); int PS4_SYSV_ABI sceHttpCreateTemplate(); int PS4_SYSV_ABI sceHttpDbgEnableProfile(); @@ -62,7 +70,7 @@ int PS4_SYSV_ABI sceHttpDeleteRequest(); int PS4_SYSV_ABI sceHttpDeleteTemplate(); int PS4_SYSV_ABI sceHttpDestroyEpoll(); int PS4_SYSV_ABI sceHttpGetAcceptEncodingGZIPEnabled(); -int PS4_SYSV_ABI sceHttpGetAllResponseHeaders(); +int PS4_SYSV_ABI sceHttpGetAllResponseHeaders(int reqId, char** header, u64* headerSize); int PS4_SYSV_ABI sceHttpGetAuthEnabled(); int PS4_SYSV_ABI sceHttpGetAutoRedirect(); int PS4_SYSV_ABI sceHttpGetConnectionStat(); @@ -76,10 +84,13 @@ int PS4_SYSV_ABI sceHttpGetMemoryPoolStats(); int PS4_SYSV_ABI sceHttpGetNonblock(); int PS4_SYSV_ABI sceHttpGetRegisteredCtxIds(); int PS4_SYSV_ABI sceHttpGetResponseContentLength(); -int PS4_SYSV_ABI sceHttpGetStatusCode(); -int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, std::size_t poolSize); -int PS4_SYSV_ABI sceHttpParseResponseHeader(); -int PS4_SYSV_ABI sceHttpParseStatusLine(); +int PS4_SYSV_ABI sceHttpGetStatusCode(int reqId, int* statusCode); +int PS4_SYSV_ABI sceHttpInit(int libnetMemId, int libsslCtxId, u64 poolSize); +int PS4_SYSV_ABI sceHttpParseResponseHeader(const char* header, u64 headerLen, const char* fieldStr, + const char** fieldValue, u64* valueLen); +int PS4_SYSV_ABI sceHttpParseStatusLine(const char* statusLine, u64 lineLen, int32_t* httpMajorVer, + int32_t* httpMinorVer, int32_t* responseCode, + const char** reasonPhrase, u64* phraseLen); int PS4_SYSV_ABI sceHttpReadData(); int PS4_SYSV_ABI sceHttpRedirectCacheFlush(); int PS4_SYSV_ABI sceHttpRemoveRequestHeader(); @@ -88,7 +99,7 @@ int PS4_SYSV_ABI sceHttpsDisableOption(); int PS4_SYSV_ABI sceHttpsDisableOptionPrivate(); int PS4_SYSV_ABI sceHttpsEnableOption(); int PS4_SYSV_ABI sceHttpsEnableOptionPrivate(); -int PS4_SYSV_ABI sceHttpSendRequest(); +int PS4_SYSV_ABI sceHttpSendRequest(int reqId, const void* postData, u64 size); int PS4_SYSV_ABI sceHttpSetAcceptEncodingGZIPEnabled(); int PS4_SYSV_ABI sceHttpSetAuthEnabled(); int PS4_SYSV_ABI sceHttpSetAuthInfoCallback(); @@ -134,14 +145,16 @@ int PS4_SYSV_ABI sceHttpTerm(); int PS4_SYSV_ABI sceHttpTryGetNonblock(); int PS4_SYSV_ABI sceHttpTrySetNonblock(); int PS4_SYSV_ABI sceHttpUnsetEpoll(); -int PS4_SYSV_ABI sceHttpUriBuild(); +int PS4_SYSV_ABI sceHttpUriBuild(char* out, u64* require, u64 prepare, + const OrbisHttpUriElement* srcElement, u32 option); int PS4_SYSV_ABI sceHttpUriCopy(); int PS4_SYSV_ABI sceHttpUriEscape(); -int PS4_SYSV_ABI sceHttpUriMerge(); +int PS4_SYSV_ABI sceHttpUriMerge(char* mergedUrl, char* url, char* relativeUri, u64* require, + u64 prepare, u32 option); int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool, - size_t* require, size_t prepare); -int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, size_t srcSize); -int PS4_SYSV_ABI sceHttpUriUnescape(char* out, size_t* require, size_t prepare, const char* in); + u64* require, u64 prepare); +int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, u64 srcSize); +int PS4_SYSV_ABI sceHttpUriUnescape(char* out, u64* require, u64 prepare, const char* in); int PS4_SYSV_ABI sceHttpWaitRequest(); void RegisterLib(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index d9ef76afc..97005813b 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -1285,7 +1285,8 @@ u16 PS4_SYSV_ABI sceNetNtohs(u16 net16) { int PS4_SYSV_ABI sceNetPoolCreate(const char* name, int size, int flags) { LOG_ERROR(Lib_Net, "(DUMMY) name = {} size = {} flags = {} ", std::string(name), size, flags); - return ORBIS_OK; + static s32 id = 1; + return id++; } int PS4_SYSV_ABI sceNetPoolDestroy() { diff --git a/src/core/libraries/np/trophy_ui.cpp b/src/core/libraries/np/trophy_ui.cpp index 94e7eb5f2..b803403c4 100644 --- a/src/core/libraries/np/trophy_ui.cpp +++ b/src/core/libraries/np/trophy_ui.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -92,59 +91,45 @@ TrophyUI::TrophyUI(const std::filesystem::path& trophyIconPath, const std::strin AddLayer(this); - bool customsoundplayed = false; -#ifdef ENABLE_QT_GUI - QString musicPathWav = QString::fromStdString(CustomTrophy_Dir.string() + "/trophy.wav"); - QString musicPathMp3 = QString::fromStdString(CustomTrophy_Dir.string() + "/trophy.mp3"); - if (fs::exists(musicPathWav.toStdString())) { - BackgroundMusicPlayer::getInstance().setVolume(100); - BackgroundMusicPlayer::getInstance().playMusic(musicPathWav, false); - customsoundplayed = true; - } else if (fs::exists(musicPathMp3.toStdString())) { - BackgroundMusicPlayer::getInstance().setVolume(100); - BackgroundMusicPlayer::getInstance().playMusic(musicPathMp3, false); - customsoundplayed = true; + MIX_Init(); + mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL); + if (!mixer) { + LOG_ERROR(Lib_NpTrophy, "Could not initialize SDL Mixer, {}", SDL_GetError()); + return; } -#endif - if (!customsoundplayed) { + MIX_SetMasterGain(mixer, static_cast(Config::getVolumeSlider() / 100.f)); + auto musicPathMp3 = CustomTrophy_Dir / "trophy.mp3"; + auto musicPathWav = CustomTrophy_Dir / "trophy.wav"; + + if (std::filesystem::exists(musicPathMp3)) { + audio = MIX_LoadAudio(mixer, musicPathMp3.string().c_str(), false); + } else if (std::filesystem::exists(musicPathWav)) { + audio = MIX_LoadAudio(mixer, musicPathWav.string().c_str(), false); + } else { auto soundFile = resource.open("src/images/trophy.wav"); std::vector soundData = std::vector(soundFile.begin(), soundFile.end()); + audio = + MIX_LoadAudio_IO(mixer, SDL_IOFromMem(soundData.data(), soundData.size()), false, true); + // due to low volume of default sound file + MIX_SetMasterGain(mixer, MIX_GetMasterGain(mixer) * 1.3f); + } - SDL_AudioSpec spec; - Uint8* audioBuf; - Uint32 audioLen; + if (!audio) { + LOG_ERROR(Lib_NpTrophy, "Could not loud audio file, {}", SDL_GetError()); + return; + } - if (!SDL_LoadWAV_IO(SDL_IOFromMem(soundData.data(), soundData.size()), true, &spec, - &audioBuf, &audioLen)) { - LOG_ERROR(Lib_NpTrophy, "Cannot load trophy sound: {}", SDL_GetError()); - SDL_free(audioBuf); - return; - } - - SDL_AudioStream* stream = - SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, nullptr, nullptr); - if (!stream) { - LOG_ERROR(Lib_NpTrophy, "Cannot create audio stream for trophy sound: {}", - SDL_GetError()); - SDL_free(audioBuf); - return; - } - - if (!SDL_PutAudioStreamData(stream, audioBuf, audioLen)) { - LOG_ERROR(Lib_NpTrophy, "Cannot add trophy sound data to stream: {}", SDL_GetError()); - SDL_free(audioBuf); - return; - } - - // Set audio gain 20% higher since audio file itself is soft - SDL_SetAudioStreamGain(stream, Config::getVolumeSlider() / 100.0f * 1.2f); - SDL_ResumeAudioStreamDevice(stream); - SDL_free(audioBuf); + if (!MIX_PlayAudio(mixer, audio)) { + LOG_ERROR(Lib_NpTrophy, "Could not play audio file, {}", SDL_GetError()); } } TrophyUI::~TrophyUI() { + MIX_DestroyAudio(audio); + MIX_DestroyMixer(mixer); + MIX_Quit(); + Finish(); } diff --git a/src/core/libraries/np/trophy_ui.h b/src/core/libraries/np/trophy_ui.h index fbadac8f4..2734471b3 100644 --- a/src/core/libraries/np/trophy_ui.h +++ b/src/core/libraries/np/trophy_ui.h @@ -5,6 +5,7 @@ #include #include +#include #include #include "common/fixed_value.h" @@ -30,6 +31,9 @@ private: std::string_view trophy_type; ImGui::RefCountedTexture trophy_icon; ImGui::RefCountedTexture trophy_type_icon; + + MIX_Mixer* mixer; + MIX_Audio* audio; }; struct TrophyInfo { diff --git a/src/core/libraries/save_data/save_instance.cpp b/src/core/libraries/save_data/save_instance.cpp index 05253eb23..75a644fdb 100644 --- a/src/core/libraries/save_data/save_instance.cpp +++ b/src/core/libraries/save_data/save_instance.cpp @@ -180,7 +180,7 @@ void SaveInstance::SetupAndMount(bool read_only, bool copy_icon, bool ignore_cor } if (!ignore_corrupt && !read_only) { - Common::FS::IOFile f(corrupt_file_path, Common::FS::FileAccessMode::Write); + Common::FS::IOFile f(corrupt_file_path, Common::FS::FileAccessMode::Create); f.Close(); } diff --git a/src/core/libraries/save_data/save_memory.cpp b/src/core/libraries/save_data/save_memory.cpp index 4080362eb..5f5ba8fea 100644 --- a/src/core/libraries/save_data/save_memory.cpp +++ b/src/core/libraries/save_data/save_memory.cpp @@ -59,7 +59,7 @@ void PersistMemory(u32 slot_id, bool lock) { while (n++ < 10) { try { IOFile f; - int r = f.Open(memoryPath, Common::FS::FileAccessMode::Write); + int r = f.Open(memoryPath, Common::FS::FileAccessMode::Create); if (f.IsOpen()) { f.WriteRaw(data.memory_cache.data(), data.memory_cache.size()); f.Close(); @@ -148,7 +148,7 @@ void SetIcon(u32 slot_id, void* buf, size_t buf_size) { fs::copy_file(src_icon, icon_path); } } else { - IOFile file(icon_path, Common::FS::FileAccessMode::Write); + IOFile file(icon_path, Common::FS::FileAccessMode::Create); file.WriteRaw(buf, buf_size); file.Close(); } diff --git a/src/core/libraries/save_data/savedata.cpp b/src/core/libraries/save_data/savedata.cpp index aec7c1d54..7fba8ed21 100644 --- a/src/core/libraries/save_data/savedata.cpp +++ b/src/core/libraries/save_data/savedata.cpp @@ -1389,7 +1389,7 @@ Error PS4_SYSV_ABI sceSaveDataSaveIcon(const OrbisSaveDataMountPoint* mountPoint } try { - const Common::FS::IOFile file(path, Common::FS::FileAccessMode::Write); + const Common::FS::IOFile file(path, Common::FS::FileAccessMode::Create); file.WriteRaw(icon->buf, std::min(icon->bufSize, icon->dataSize)); } catch (const fs::filesystem_error& e) { LOG_ERROR(Lib_SaveData, "Failed to load icon: {}", e.what()); diff --git a/src/core/libraries/usbd/emulated/dimensions.cpp b/src/core/libraries/usbd/emulated/dimensions.cpp new file mode 100644 index 000000000..272f2f649 --- /dev/null +++ b/src/core/libraries/usbd/emulated/dimensions.cpp @@ -0,0 +1,651 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "dimensions.h" + +#include +#include + +namespace Libraries::Usbd { + +static constexpr std::array COMMAND_KEY = {0x55, 0xFE, 0xF6, 0xB0, 0x62, 0xBF, 0x0B, 0x41, + 0xC9, 0xB3, 0x7C, 0xB4, 0x97, 0x3E, 0x29, 0x7B}; + +static constexpr std::array CHAR_CONSTANT = {0xB7, 0xD5, 0xD7, 0xE6, 0xE7, 0xBA, + 0x3C, 0xA8, 0xD8, 0x75, 0x47, 0x68, + 0xCF, 0x23, 0xE9, 0xFE, 0xAA}; + +static constexpr std::array 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 data; + ASSERT(file.Read(data) == data.size()); + LoadDimensionsFigure(data, std::move(file), pad, index); +} + +u32 DimensionsToypad::LoadDimensionsFigure(const std::array& 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 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 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 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 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 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& 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& 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& reply_buf) { + // Decrypt payload into an 8 byte array + const std::array 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 value_to_encrypt = {value[4], value[5], value[6], value[7], 0, 0, 0, 0}; + const std::array 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 DimensionsToypad::Decrypt(const u8* buf, std::optional> 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 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 DimensionsToypad::Encrypt(const u8* buf, std::optional> 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 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 DimensionsToypad::GenerateFigureKey(const std::array& buf) { + std::array 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& uid, u8 count) { + std::vector 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 randomized = DimensionsRandomize(to_scramble, count); + + return (u32(randomized[0]) << 24) | (u32(randomized[1]) << 16) | (u32(randomized[2]) << 8) | + u32(randomized[3]); +} + +std::array DimensionsToypad::PWDGenerate(const std::array& uid) { + std::vector 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 DimensionsToypad::DimensionsRandomize(const std::vector& 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& buf) { + const std::array figure_key = GenerateFigureKey(buf); + + const std::array 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& reply_buf) { + // Decrypt payload into an 8 byte array + const std::array value = Decrypt(buf, std::nullopt); + // Confirmation is the first 4 bytes of the decrypted payload + // const u32 conf = read_from_ptr>(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 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 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& 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& 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& reply_buf) { + // Decrypt payload to 8 byte array, byte 1 is the index, 4-7 are the confirmation + const std::array value = Decrypt(buf, std::nullopt); + const u8 index = value[0]; + // const u32 conf = read_from_ptr>(value, 4); + std::array 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 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> DimensionsToypad::PopAddedRemovedResponse() { + std::lock_guard lock(m_dimensions_mutex); + + if (m_figure_added_removed_responses.empty()) { + return std::nullopt; + } + + std::array 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> 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 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 \ No newline at end of file diff --git a/src/core/libraries/usbd/emulated/dimensions.h b/src/core/libraries/usbd/emulated/dimensions.h new file mode 100644 index 000000000..d9573b5f4 --- /dev/null +++ b/src/core/libraries/usbd/emulated/dimensions.h @@ -0,0 +1,118 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include + +#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 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& reply_buf); + void GenerateRandomNumber(const u8* buf, u8 sequence, std::array& reply_buf); + void InitializeRNG(u32 seed); + void GetChallengeResponse(const u8* buf, u8 sequence, std::array& reply_buf); + void QueryBlock(u8 index, u8 page, std::array& reply_buf, u8 sequence); + void WriteBlock(u8 index, u8 page, const u8* to_write_buf, std::array& reply_buf, + u8 sequence); + void GetModel(const u8* buf, u8 sequence, std::array& reply_buf); + std::optional> 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& buf, Common::FS::IOFile file, + u8 pad, u8 index); + +protected: + std::mutex m_dimensions_mutex; + std::array m_figures{}; + +private: + static void RandomUID(u8* uid_buffer); + static u8 GenerateChecksum(const std::array& data, u32 num_of_bytes); + static std::array Decrypt(const u8* buf, std::optional> key); + static std::array Encrypt(const u8* buf, std::optional> key); + static std::array GenerateFigureKey(const std::array& buf); + static u32 Scramble(const std::array& uid, u8 count); + static std::array PWDGenerate(const std::array& uid); + static std::array DimensionsRandomize(const std::vector& key, u8 count); + static u32 GetFigureId(const std::array& 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> 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 GetImplRef() override { + return m_dimensions_toypad; + } + + std::mutex m_query_mutex; + std::queue> m_queries; + +private: + std::shared_ptr m_dimensions_toypad = std::make_shared(); + + std::array m_endpoint_out_extra = {0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x1d, 0x00}; + std::vector m_endpoint_descriptors = { + {0x7, 0x5, 0x81, 0x3, 0x20, 0x1, 0x0, 0x0}, {0x7, 0x5, 0x1, 0x3, 0x20, 0x1, 0x0, 0x0}}; + std::vector m_interface_descriptors = { + {0x9, 0x4, 0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0}}; + std::vector m_config_descriptors = { + {0x9, 0x2, 0x29, 0x1, 0x1, 0x0, 0x80, 0xFA}}; + std::vector m_device_descriptors = { + {0x12, 0x1, 0x200, 0x0, 0x0, 0x0, 0x20, 0x0E6F, 0x0241, 0x200, 0x1, 0x2, 0x0, 0x1}}; +}; +} // namespace Libraries::Usbd \ No newline at end of file diff --git a/src/core/libraries/usbd/emulated/infinity.cpp b/src/core/libraries/usbd/emulated/infinity.cpp new file mode 100644 index 000000000..af8e13dcc --- /dev/null +++ b/src/core/libraries/usbd/emulated/infinity.cpp @@ -0,0 +1,392 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "infinity.h" + +#include + +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 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 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& 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 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& 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& 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& 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& 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& 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& 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& 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> InfinityBase::PopAddedRemovedResponse() { + if (m_figure_added_removed_responses.empty()) + return std::nullopt; + + std::array response = m_figure_added_removed_responses.front(); + m_figure_added_removed_responses.pop(); + return response; +} + +u8 InfinityBase::GenerateChecksum(const std::array& 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> 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 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 \ No newline at end of file diff --git a/src/core/libraries/usbd/emulated/infinity.h b/src/core/libraries/usbd/emulated/infinity.h new file mode 100644 index 000000000..4c8a5305f --- /dev/null +++ b/src/core/libraries/usbd/emulated/infinity.h @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include + +#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 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& reply_buf); + void DescrambleAndSeed(u8* buf, u8 sequence, std::array& reply_buf); + void GetNextAndScramble(u8 sequence, std::array& reply_buf); + void GetPresentFigures(u8 sequence, std::array& reply_buf); + void QueryBlock(u8 fig_num, u8 block, std::array& reply_buf, u8 sequence); + void WriteBlock(u8 fig_num, u8 block, const u8* to_write_buf, std::array& reply_buf, + u8 sequence); + void GetFigureIdentifier(u8 fig_num, u8 sequence, std::array& reply_buf); + std::optional> 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& buf, Common::FS::IOFile file, + u8 position); + +protected: + std::mutex infinity_mutex; + std::array infinity_figures; + +private: + u8 GenerateChecksum(const std::array& 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> 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 GetImplRef() override { + return m_infinity_base; + } + +private: + std::shared_ptr m_infinity_base = std::make_shared(); + + std::array m_endpoint_out_extra = {0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x1d, 0x00}; + std::vector 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 m_interface_descriptors = { + {0x9, 0x4, 0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0}}; + std::vector m_config_descriptors = { + {0x9, 0x2, 0x29, 0x1, 0x1, 0x0, 0x80, 0xFA}}; + std::vector m_device_descriptors = { + {0x12, 0x1, 0x200, 0x0, 0x0, 0x0, 0x20, 0x0E6F, 0x0129, 0x200, 0x1, 0x2, 0x3, 0x1}}; + + std::queue> m_queries; +}; +} // namespace Libraries::Usbd \ No newline at end of file diff --git a/src/core/libraries/usbd/usb_backend.h b/src/core/libraries/usbd/usb_backend.h index 7a5c2899a..e668d5509 100644 --- a/src/core/libraries/usbd/usb_backend.h +++ b/src/core/libraries/usbd/usb_backend.h @@ -312,7 +312,7 @@ public: const auto endpoint_descs = FillEndpointDescriptorPair(); const auto interface_desc = FillInterfaceDescriptor(endpoint_descs); - const auto interface = static_cast(calloc(1, sizeof(libusb_interface*))); + const auto interface = static_cast(calloc(1, sizeof(libusb_interface))); interface->altsetting = interface_desc; interface->num_altsetting = 1; @@ -366,7 +366,7 @@ public: const auto desc = FillDeviceDescriptor(); ASSERT(desc); - const auto fake = static_cast(calloc(1, sizeof(UsbDevice*))); + const auto fake = static_cast(calloc(1, sizeof(UsbDevice))); fake->bus_number = 0; fake->port_number = 0; fake->device_address = 0; diff --git a/src/core/libraries/usbd/usbd.h b/src/core/libraries/usbd/usbd.h index 60a3e753b..fef9d6871 100644 --- a/src/core/libraries/usbd/usbd.h +++ b/src/core/libraries/usbd/usbd.h @@ -4,6 +4,8 @@ #pragma once #include "common/types.h" +#include "emulated/dimensions.h" +#include "emulated/infinity.h" #include "emulated/skylander.h" #include "usb_backend.h" @@ -33,10 +35,9 @@ using SceUsbdTransfer = libusb_transfer; using SceUsbdControlSetup = libusb_control_setup; using SceUsbdTransferCallback = void PS4_SYSV_ABI (*)(SceUsbdTransfer* transfer); -// TODO: implement emulated devices using SkylandersPortalBackend = SkylanderBackend; -using InfinityBaseBackend = UsbRealBackend; -using DimensionsToypadBackend = UsbRealBackend; +using InfinityBaseBackend = InfinityBackend; +using DimensionsToypadBackend = DimensionsBackend; enum class SceUsbdSpeed : u32 { UNKNOWN = 0, diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 4de20436f..ed3f43c1c 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -502,19 +502,19 @@ bool Elf::IsSharedLib() { } void Elf::ElfHeaderDebugDump(const std::filesystem::path& file_name) { - Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write, + Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create, Common::FS::FileType::TextFile}; f.WriteString(ElfHeaderStr()); } void Elf::SelfHeaderDebugDump(const std::filesystem::path& file_name) { - Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write, + Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create, Common::FS::FileType::TextFile}; f.WriteString(SElfHeaderStr()); } void Elf::SelfSegHeaderDebugDump(const std::filesystem::path& file_name) { - Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write, + Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create, Common::FS::FileType::TextFile}; for (u16 i = 0; i < m_self.segment_count; i++) { f.WriteString(SELFSegHeader(i)); @@ -522,7 +522,7 @@ void Elf::SelfSegHeaderDebugDump(const std::filesystem::path& file_name) { } void Elf::PHeaderDebugDump(const std::filesystem::path& file_name) { - Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write, + Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create, Common::FS::FileType::TextFile}; if (m_elf_header.e_phentsize > 0) { for (u16 i = 0; i < m_elf_header.e_phnum; i++) { diff --git a/src/core/loader/symbols_resolver.cpp b/src/core/loader/symbols_resolver.cpp index 09c7fae8a..9fef1a2a2 100644 --- a/src/core/loader/symbols_resolver.cpp +++ b/src/core/loader/symbols_resolver.cpp @@ -32,7 +32,7 @@ const SymbolRecord* SymbolsResolver::FindSymbol(const SymbolResolver& s) const { } void SymbolsResolver::DebugDump(const std::filesystem::path& file_name) { - Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Write, + Common::FS::IOFile f{file_name, Common::FS::FileAccessMode::Create, Common::FS::FileType::TextFile}; for (const auto& symbol : m_symbols) { const auto ids = Common::SplitString(symbol.name, '#'); diff --git a/src/emulator.cpp b/src/emulator.cpp index f9e1f7959..ad407f9b6 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -31,6 +31,8 @@ #include "core/file_format/trp.h" #include "core/file_sys/fs.h" #include "core/libraries/disc_map/disc_map.h" +#include "core/libraries/font/font.h" +#include "core/libraries/font/fontft.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libs.h" #include "core/libraries/ngs2/ngs2.h" @@ -111,6 +113,7 @@ void Emulator::Run(std::filesystem::path file, std::vector args, std::string id; std::string title; std::string app_version; + u32 sdk_version; u32 fw_version; Common::PSFAttributes psf_attributes{}; if (param_sfo_exists) { @@ -130,8 +133,48 @@ void Emulator::Run(std::filesystem::path file, std::vector args, if (const auto raw_attributes = param_sfo->GetInteger("ATTRIBUTE")) { psf_attributes.raw = *raw_attributes; } + + // Extract sdk version from pubtool info. + std::string_view pubtool_info = + param_sfo->GetString("PUBTOOLINFO").value_or("Unknown value"); + u64 sdk_ver_offset = pubtool_info.find("sdk_ver"); + + if (sdk_ver_offset == pubtool_info.npos) { + // Default to using firmware version if SDK version is not found. + sdk_version = fw_version; + } else { + // Increment offset to account for sdk_ver= part of string. + sdk_ver_offset += 8; + u64 sdk_ver_len = pubtool_info.find(",", sdk_ver_offset); + if (sdk_ver_len == pubtool_info.npos) { + // If there's no more commas, this is likely the last entry of pubtool info. + // Use string length instead. + sdk_ver_len = pubtool_info.size(); + } + sdk_ver_len -= sdk_ver_offset; + std::string sdk_ver_string = pubtool_info.substr(sdk_ver_offset, sdk_ver_len).data(); + // Number is stored in base 16. + sdk_version = std::stoi(sdk_ver_string, nullptr, 16); + } } + auto& game_info = Common::ElfInfo::Instance(); + game_info.initialized = true; + game_info.game_serial = id; + game_info.title = title; + game_info.app_ver = app_version; + game_info.firmware_ver = fw_version & 0xFFF00000; + game_info.raw_firmware_ver = fw_version; + game_info.sdk_ver = sdk_version; + game_info.psf_attributes = psf_attributes; + + const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png"); + if (std::filesystem::exists(pic1_path)) { + game_info.splash_path = pic1_path; + } + + game_info.game_folder = game_folder; + Config::load(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (id + ".toml"), true); @@ -194,6 +237,7 @@ void Emulator::Run(std::filesystem::path file, std::vector args, if (param_sfo_exists) { LOG_INFO(Loader, "Game id: {} Title: {}", id, title); LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version); + LOG_INFO(Loader, "Compiled SDK version: {:#x}", sdk_version); LOG_INFO(Loader, "PSVR Supported: {}", (bool)psf_attributes.support_ps_vr.Value()); LOG_INFO(Loader, "PSVR Required: {}", (bool)psf_attributes.require_ps_vr.Value()); } @@ -233,22 +277,6 @@ void Emulator::Run(std::filesystem::path file, std::vector args, } } - auto& game_info = Common::ElfInfo::Instance(); - game_info.initialized = true; - game_info.game_serial = id; - game_info.title = title; - game_info.app_ver = app_version; - game_info.firmware_ver = fw_version & 0xFFF00000; - game_info.raw_firmware_ver = fw_version; - game_info.psf_attributes = psf_attributes; - - const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png"); - if (std::filesystem::exists(pic1_path)) { - game_info.splash_path = pic1_path; - } - - game_info.game_folder = game_folder; - std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version); std::string window_title = ""; std::string remote_url(Common::g_scm_remote_url); @@ -473,8 +501,8 @@ void Emulator::LoadSystemModules(const std::string& game_serial) { {"libSceJson2.sprx", nullptr}, {"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterLib}, {"libSceCesCs.sprx", nullptr}, - {"libSceFont.sprx", nullptr}, - {"libSceFontFt.sprx", nullptr}, + {"libSceFont.sprx", &Libraries::Font::RegisterlibSceFont}, + {"libSceFontFt.sprx", &Libraries::FontFt::RegisterlibSceFontFt}, {"libSceFreeTypeOt.sprx", nullptr}}); std::vector found_modules; diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index f82612444..452dee013 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -250,7 +250,7 @@ void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view, } bool MustKeepDrawing() { - return layers.size() > 1 || DebugState.IsShowingDebugMenuBar(); + return layers.size() > 1 || change_layers.size() > 1 || DebugState.IsShowingDebugMenuBar(); } } // namespace Core diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index 668882254..57b50a3e1 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -559,7 +559,7 @@ void Translator::EmitFetch(const GcnInst& inst) { std::filesystem::create_directories(dump_dir); } const auto filename = fmt::format("vs_{:#018x}.fetch.bin", info.pgm_hash); - const auto file = IOFile{dump_dir / filename, FileAccessMode::Write}; + const auto file = IOFile{dump_dir / filename, FileAccessMode::Create}; file.WriteRaw(fetch_data->code, fetch_data->size); } diff --git a/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp b/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp index 1d3b46b43..7626b9c9f 100644 --- a/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp +++ b/src/shader_recompiler/ir/passes/flatten_extended_userdata_pass.cpp @@ -39,7 +39,7 @@ static void DumpSrtProgram(const Shader::Info& info, const u8* code, size_t code std::filesystem::create_directories(dump_dir); } const auto filename = fmt::format("{}_{:#018x}.srtprogram.txt", info.stage, info.pgm_hash); - const auto file = IOFile{dump_dir / filename, FileAccessMode::Write, FileType::TextFile}; + const auto file = IOFile{dump_dir / filename, FileAccessMode::Create, FileType::TextFile}; u64 address = reinterpret_cast(code); u64 code_end = address + codesize; diff --git a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp index 3df4f8b86..c298a1092 100644 --- a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp +++ b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -190,7 +190,7 @@ void CollectShaderInfoPass(IR::Program& program, const Profile& profile) { }); info.buffers.push_back({ .used_types = IR::Type::U32, - .inline_cbuf = AmdGpu::Buffer::Placeholder(VideoCore::BufferCache::FAULT_BUFFER_SIZE), + .inline_cbuf = AmdGpu::Buffer::Placeholder(std::numeric_limits::max()), .buffer_type = BufferType::FaultBuffer, .is_written = true, }); diff --git a/src/shader_recompiler/ir/program.cpp b/src/shader_recompiler/ir/program.cpp index f2f6e34fa..1d03ea9ab 100644 --- a/src/shader_recompiler/ir/program.cpp +++ b/src/shader_recompiler/ir/program.cpp @@ -28,7 +28,7 @@ void DumpProgram(const Program& program, const Info& info, const std::string& ty } const auto ir_filename = fmt::format("{}_{:#018x}.{}irprogram.txt", info.stage, info.pgm_hash, type); - const auto ir_file = IOFile{dump_dir / ir_filename, FileAccessMode::Write, FileType::TextFile}; + const auto ir_file = IOFile{dump_dir / ir_filename, FileAccessMode::Create, FileType::TextFile}; size_t index{0}; std::map inst_to_index; @@ -46,7 +46,7 @@ void DumpProgram(const Program& program, const Info& info, const std::string& ty const auto asl_filename = fmt::format("{}_{:#018x}.{}asl.txt", info.stage, info.pgm_hash, type); const auto asl_file = - IOFile{dump_dir / asl_filename, FileAccessMode::Write, FileType::TextFile}; + IOFile{dump_dir / asl_filename, FileAccessMode::Create, FileType::TextFile}; for (const auto& node : program.syntax_list) { std::string s = IR::DumpASLNode(node, block_to_index, inst_to_index) + '\n'; diff --git a/src/video_core/buffer_cache/buffer_cache.cpp b/src/video_core/buffer_cache/buffer_cache.cpp index 04c473f1b..ac3fac5b1 100644 --- a/src/video_core/buffer_cache/buffer_cache.cpp +++ b/src/video_core/buffer_cache/buffer_cache.cpp @@ -2,49 +2,42 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include -#include #include "common/alignment.h" #include "common/debug.h" #include "common/scope_exit.h" -#include "common/types.h" #include "core/memory.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/buffer_cache/buffer_cache.h" #include "video_core/buffer_cache/memory_tracker.h" -#include "video_core/host_shaders/fault_buffer_process_comp.h" #include "video_core/renderer_vulkan/vk_graphics_pipeline.h" #include "video_core/renderer_vulkan/vk_instance.h" -#include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_scheduler.h" -#include "video_core/renderer_vulkan/vk_shader_util.h" #include "video_core/texture_cache/texture_cache.h" namespace VideoCore { static constexpr size_t DataShareBufferSize = 64_KB; static constexpr size_t StagingBufferSize = 512_MB; +static constexpr size_t DownloadBufferSize = 32_MB; static constexpr size_t UboStreamBufferSize = 64_MB; -static constexpr size_t DownloadBufferSize = 128_MB; static constexpr size_t DeviceBufferSize = 128_MB; -static constexpr size_t MaxPageFaults = 1024; BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, AmdGpu::Liverpool* liverpool_, TextureCache& texture_cache_, PageManager& tracker) : instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, memory{Core::Memory::Instance()}, texture_cache{texture_cache_}, + fault_manager{instance, scheduler, *this, CACHING_PAGEBITS, CACHING_NUMPAGES}, staging_buffer{instance, scheduler, MemoryUsage::Upload, StagingBufferSize}, stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize}, download_buffer{instance, scheduler, MemoryUsage::Download, DownloadBufferSize}, device_buffer{instance, scheduler, MemoryUsage::DeviceLocal, DeviceBufferSize}, gds_buffer{instance, scheduler, MemoryUsage::Stream, 0, AllFlags, DataShareBufferSize}, bda_pagetable_buffer{instance, scheduler, MemoryUsage::DeviceLocal, - 0, AllFlags, BDA_PAGETABLE_SIZE}, - fault_buffer(instance, scheduler, MemoryUsage::DeviceLocal, 0, AllFlags, FAULT_BUFFER_SIZE) { + 0, AllFlags, BDA_PAGETABLE_SIZE} { Vulkan::SetObjectName(instance.GetDevice(), gds_buffer.Handle(), "GDS Buffer"); Vulkan::SetObjectName(instance.GetDevice(), bda_pagetable_buffer.Handle(), "BDA Page Table Buffer"); - Vulkan::SetObjectName(instance.GetDevice(), fault_buffer.Handle(), "Fault Buffer"); memory_tracker = std::make_unique(tracker); @@ -57,80 +50,6 @@ BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& s const vk::Buffer& null_buffer = slot_buffers[null_id].buffer; Vulkan::SetObjectName(instance.GetDevice(), null_buffer, "Null Buffer"); - // Prepare the fault buffer parsing pipeline - boost::container::static_vector 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(bindings.size()), - .pBindings = bindings.data(), - }; - auto [desc_layout_result, desc_layout] = - instance.GetDevice().createDescriptorSetLayoutUnique(desc_layout_ci); - ASSERT_MSG(desc_layout_result == vk::Result::eSuccess, - "Failed to create descriptor set layout: {}", vk::to_string(desc_layout_result)); - fault_process_desc_layout = std::move(desc_layout); - - const auto& module = Vulkan::Compile(HostShaders::FAULT_BUFFER_PROCESS_COMP, - vk::ShaderStageFlagBits::eCompute, instance.GetDevice()); - Vulkan::SetObjectName(instance.GetDevice(), module, "Fault Buffer Parser"); - - const vk::SpecializationMapEntry specialization_map_entry = { - .constantID = 0, - .offset = 0, - .size = sizeof(u32), - }; - - const vk::SpecializationInfo specialization_info = { - .mapEntryCount = 1, - .pMapEntries = &specialization_map_entry, - .dataSize = sizeof(u32), - .pData = &CACHING_PAGEBITS, - }; - - const vk::PipelineShaderStageCreateInfo shader_ci = { - .stage = vk::ShaderStageFlagBits::eCompute, - .module = module, - .pName = "main", - .pSpecializationInfo = &specialization_info, - }; - - const vk::PipelineLayoutCreateInfo layout_info = { - .setLayoutCount = 1U, - .pSetLayouts = &(*fault_process_desc_layout), - }; - auto [layout_result, layout] = instance.GetDevice().createPipelineLayoutUnique(layout_info); - ASSERT_MSG(layout_result == vk::Result::eSuccess, "Failed to create pipeline layout: {}", - vk::to_string(layout_result)); - fault_process_pipeline_layout = std::move(layout); - - const vk::ComputePipelineCreateInfo pipeline_info = { - .stage = shader_ci, - .layout = *fault_process_pipeline_layout, - }; - auto [pipeline_result, pipeline] = - instance.GetDevice().createComputePipelineUnique({}, pipeline_info); - ASSERT_MSG(pipeline_result == vk::Result::eSuccess, "Failed to create compute pipeline: {}", - vk::to_string(pipeline_result)); - fault_process_pipeline = std::move(pipeline); - Vulkan::SetObjectName(instance.GetDevice(), *fault_process_pipeline, - "Fault Buffer Parser Pipeline"); - - instance.GetDevice().destroyShaderModule(module); - // Set up garbage collection parameters if (!instance.CanReportMemoryUsage()) { trigger_gc_memory = DEFAULT_TRIGGER_GC_MEMORY; @@ -656,14 +575,10 @@ BufferId BufferCache::CreateBuffer(VAddr device_addr, u32 wanted_size) { wanted_size = static_cast(device_addr_end - device_addr); const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size); const u32 size = static_cast(overlap.end - overlap.begin); - const BufferId new_buffer_id = [&] { - std::scoped_lock lk{slot_buffers_mutex}; - return slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin, - AllFlags | vk::BufferUsageFlagBits::eShaderDeviceAddress, size); - }(); + const BufferId new_buffer_id = + slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin, + AllFlags | vk::BufferUsageFlagBits::eShaderDeviceAddress, size); auto& new_buffer = slot_buffers[new_buffer_id]; - const size_t size_bytes = new_buffer.SizeBytes(); - const auto cmdbuf = scheduler.CommandBuffer(); for (const BufferId overlap_id : overlap.ids) { JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); } @@ -672,126 +587,7 @@ BufferId BufferCache::CreateBuffer(VAddr device_addr, u32 wanted_size) { } void BufferCache::ProcessFaultBuffer() { - // Run fault processing shader - const auto [mapped, offset] = download_buffer.Map(MaxPageFaults * sizeof(u64)); - vk::BufferMemoryBarrier2 fault_buffer_barrier{ - .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .srcAccessMask = vk::AccessFlagBits2::eShaderWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eComputeShader, - .dstAccessMask = vk::AccessFlagBits2::eShaderRead, - .buffer = fault_buffer.Handle(), - .offset = 0, - .size = FAULT_BUFFER_SIZE, - }; - vk::BufferMemoryBarrier2 download_barrier{ - .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eComputeShader, - .dstAccessMask = vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eShaderWrite, - .buffer = download_buffer.Handle(), - .offset = offset, - .size = MaxPageFaults * sizeof(u64), - }; - std::array 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 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 fault_ranges; - const u64* fault_ptr = std::bit_cast(mapped); - const u32 fault_count = static_cast(*(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::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::max(), - "Buffer size is too large"); - CreateBuffer(start, static_cast(end - start)); - } - }); + fault_manager.ProcessFaultBuffer(); } void BufferCache::Register(BufferId buffer_id) { @@ -889,7 +685,7 @@ bool BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size, }); TouchBuffer(buffer); } - if (is_texel_buffer) { + if (is_texel_buffer && !is_written) { return SynchronizeBufferFromImage(buffer, device_addr, size); } return false; @@ -972,10 +768,7 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, } void BufferCache::SynchronizeBuffersInRange(VAddr device_addr, u64 size) { - if (device_addr == 0) { - return; - } - VAddr device_addr_end = device_addr + size; + const VAddr device_addr_end = device_addr + size; ForEachBufferInRange(device_addr, size, [&](BufferId buffer_id, Buffer& buffer) { RENDERER_TRACE; VAddr start = std::max(buffer.CpuAddr(), device_addr); @@ -985,21 +778,6 @@ void BufferCache::SynchronizeBuffersInRange(VAddr device_addr, u64 size) { }); } -void BufferCache::MemoryBarrier() { - scheduler.EndRendering(); - const auto cmdbuf = scheduler.CommandBuffer(); - vk::MemoryBarrier2 barrier = { - .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eMemoryWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, - .dstAccessMask = vk::AccessFlagBits2::eMemoryRead, - }; - cmdbuf.pipelineBarrier2(vk::DependencyInfo{ - .memoryBarrierCount = 1, - .pMemoryBarriers = &barrier, - }); -} - void BufferCache::InlineDataBuffer(Buffer& buffer, VAddr address, const void* value, u32 num_bytes) { scheduler.EndRendering(); diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index ccf77b4f5..6954f979e 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -3,12 +3,12 @@ #pragma once -#include #include #include "common/lru_cache.h" #include "common/slot_vector.h" #include "common/types.h" #include "video_core/buffer_cache/buffer.h" +#include "video_core/buffer_cache/fault_manager.h" #include "video_core/buffer_cache/range_set.h" #include "video_core/multi_level_page_table.h" @@ -40,9 +40,7 @@ public: static constexpr u64 CACHING_PAGESIZE = u64{1} << CACHING_PAGEBITS; static constexpr u64 DEVICE_PAGESIZE = 16_KB; static constexpr u64 CACHING_NUMPAGES = u64{1} << (40 - CACHING_PAGEBITS); - static constexpr u64 BDA_PAGETABLE_SIZE = CACHING_NUMPAGES * sizeof(vk::DeviceAddress); - static constexpr u64 FAULT_BUFFER_SIZE = CACHING_NUMPAGES / 8; // Bit per page // Default values for garbage collection static constexpr s64 DEFAULT_TRIGGER_GC_MEMORY = 1_GB; @@ -68,12 +66,6 @@ public: bool has_stream_leap = false; }; - using IntervalSet = - boost::icl::interval_set; - using IntervalType = typename IntervalSet::interval_type; - public: explicit BufferCache(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler, AmdGpu::Liverpool* liverpool, TextureCache& texture_cache, @@ -92,7 +84,7 @@ public: /// Retrieves the fault buffer. [[nodiscard]] Buffer* GetFaultBuffer() noexcept { - return &fault_buffer; + return fault_manager.GetFaultBuffer(); } /// Retrieves the buffer with the specified id. @@ -160,9 +152,6 @@ public: /// Synchronizes all buffers neede for DMA. void SynchronizeDmaBuffers(); - /// Record memory barrier. Used for buffers when accessed via BDA. - void MemoryBarrier(); - /// Runs the garbage collector. void RunGarbageCollector(); @@ -217,6 +206,7 @@ private: AmdGpu::Liverpool* liverpool; Core::MemoryManager* memory; TextureCache& texture_cache; + FaultManager fault_manager; std::unique_ptr memory_tracker; StreamBuffer staging_buffer; StreamBuffer stream_buffer; @@ -224,8 +214,6 @@ private: StreamBuffer device_buffer; Buffer gds_buffer; Buffer bda_pagetable_buffer; - Buffer fault_buffer; - std::shared_mutex slot_buffers_mutex; Common::SlotVector slot_buffers; u64 total_used_memory = 0; u64 trigger_gc_memory = 0; @@ -235,9 +223,6 @@ private: RangeSet gpu_modified_ranges; SplitRangeMap buffer_ranges; PageTable page_table; - vk::UniqueDescriptorSetLayout fault_process_desc_layout; - vk::UniquePipeline fault_process_pipeline; - vk::UniquePipelineLayout fault_process_pipeline_layout; }; } // namespace VideoCore diff --git a/src/video_core/buffer_cache/fault_manager.cpp b/src/video_core/buffer_cache/fault_manager.cpp new file mode 100644 index 000000000..e967ffd0e --- /dev/null +++ b/src/video_core/buffer_cache/fault_manager.cpp @@ -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 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 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 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(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::max(), + "Buffer size is too large"); + buffer_cache.FindBuffer(start, static_cast(end - start)); + }); + fault_areas[area] = 0; + }); + + fault_areas[current_area++] = scheduler.CurrentTick(); + current_area %= MaxPendingFaults; +} + +} // namespace VideoCore diff --git a/src/video_core/buffer_cache/fault_manager.h b/src/video_core/buffer_cache/fault_manager.h new file mode 100644 index 000000000..4fd545433 --- /dev/null +++ b/src/video_core/buffer_cache/fault_manager.h @@ -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 fault_areas{}; + u32 current_area{}; + vk::UniqueDescriptorSetLayout fault_process_desc_layout; + vk::UniquePipeline fault_process_pipeline; + vk::UniquePipelineLayout fault_process_pipeline_layout; +}; + +} // namespace VideoCore diff --git a/src/video_core/host_shaders/fault_buffer_process.comp b/src/video_core/host_shaders/fault_buffer_process.comp index a712cf441..04a86bad3 100644 --- a/src/video_core/host_shaders/fault_buffer_process.comp +++ b/src/video_core/host_shaders/fault_buffer_process.comp @@ -13,30 +13,23 @@ layout(std430, binding = 0) buffer input_buf { layout(std430, binding = 1) buffer output_buf { uint64_t download_buffer[]; }; - -// Overlap for 32 bit atomics layout(std430, binding = 1) buffer output_buf32 { uint download_buffer32[]; }; -layout(constant_id = 0) const uint CACHING_PAGEBITS = 0; - void main() { - uint id = gl_GlobalInvocationID.x; + const uint id = gl_GlobalInvocationID.x; uint word = fault_buffer[id]; - if (word == 0u) { - return; - } - // 1 page per bit - uint base_bit = id * 32u; + fault_buffer[id] = 0u; + const uint base_bit = id * 32u; while (word != 0u) { - uint bit = findLSB(word); - word &= word - 1; - uint page = base_bit + bit; - uint store_index = atomicAdd(download_buffer32[0], 1u) + 1u; - // It is very unlikely, but should we check for overflow? - if (store_index < 1024u) { // only support 1024 page faults - download_buffer[store_index] = uint64_t(page) << CACHING_PAGEBITS; + const uint store_index = atomicAdd(download_buffer32[0], 1u) + 1u; + if (store_index >= MAX_PAGE_FAULTS) { + return; } + const uint bit = findLSB(word); + word &= word - 1; + const uint page = base_bit + bit; + download_buffer[store_index] = uint64_t(page) << CACHING_PAGEBITS; } } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 24daf9c1c..4706bff24 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -355,13 +355,12 @@ bool PipelineCache::RefreshGraphicsKey() { } // Fill color target information - key.color_buffers[cb] = Shader::PsColorBuffer{ - .data_format = col_buf.GetDataFmt(), - .num_format = col_buf.GetNumberFmt(), - .num_conversion = col_buf.GetNumberConversion(), - .export_format = regs.color_export_format.GetFormat(cb), - .swizzle = col_buf.Swizzle(), - }; + auto& color_buffer = key.color_buffers[cb]; + color_buffer.data_format = col_buf.GetDataFmt(); + color_buffer.num_format = col_buf.GetNumberFmt(); + color_buffer.num_conversion = col_buf.GetNumberConversion(); + color_buffer.export_format = regs.color_export_format.GetFormat(cb); + color_buffer.swizzle = col_buf.Swizzle(); } // Compile and bind shader stages @@ -379,7 +378,7 @@ bool PipelineCache::RefreshGraphicsKey() { continue; } if ((key.mrt_mask & (1u << cb)) == 0) { - key.color_buffers[cb] = {}; + std::memset(&key.color_buffers[cb], 0, sizeof(Shader::PsColorBuffer)); continue; } @@ -632,7 +631,7 @@ void PipelineCache::DumpShader(std::span code, u64 hash, Shader::Stag std::filesystem::create_directories(dump_dir); } const auto filename = fmt::format("{}.{}", GetShaderName(stage, hash, perm_idx), ext); - const auto file = IOFile{dump_dir / filename, FileAccessMode::Write}; + const auto file = IOFile{dump_dir / filename, FileAccessMode::Create}; file.WriteSpan(code); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a47c523e1..8d00ff2d0 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -380,7 +380,8 @@ void Rasterizer::OnSubmit() { } bool Rasterizer::BindResources(const Pipeline* pipeline) { - if (IsComputeImageCopy(pipeline) || IsComputeMetaClear(pipeline)) { + if (IsComputeImageCopy(pipeline) || IsComputeMetaClear(pipeline) || + IsComputeImageClear(pipeline)) { return false; } @@ -406,18 +407,13 @@ bool Rasterizer::BindResources(const Pipeline* pipeline) { if (uses_dma) { // We only use fault buffer for DMA right now. - { - Common::RecursiveSharedLock lock{mapped_ranges_mutex}; - for (auto& range : mapped_ranges) { - buffer_cache.SynchronizeBuffersInRange(range.lower(), - range.upper() - range.lower()); - } + Common::RecursiveSharedLock lock{mapped_ranges_mutex}; + for (auto& range : mapped_ranges) { + buffer_cache.SynchronizeBuffersInRange(range.lower(), range.upper() - range.lower()); } - buffer_cache.MemoryBarrier(); + fault_process_pending = true; } - fault_process_pending |= uses_dma; - return true; } @@ -520,6 +516,66 @@ bool Rasterizer::IsComputeImageCopy(const Pipeline* pipeline) { return true; } +bool Rasterizer::IsComputeImageClear(const Pipeline* pipeline) { + if (!pipeline->IsCompute()) { + return false; + } + + // Ensure shader only has 2 bound buffers + const auto& cs_pgm = liverpool->GetCsRegs(); + const auto& info = pipeline->GetStage(Shader::LogicalStage::Compute); + if (cs_pgm.num_thread_x.full != 64 || info.buffers.size() != 2 || !info.images.empty()) { + return false; + } + + // From those 2 buffers, first must hold the clear vector and second the image being cleared + const auto& desc0 = info.buffers[0]; + const auto& desc1 = info.buffers[1]; + if (desc0.is_formatted || !desc1.is_formatted || desc0.is_written || !desc1.is_written) { + return false; + } + + // First buffer must have size of vec4 and second the size of a single layer + const AmdGpu::Buffer buf0 = desc0.GetSharp(info); + const AmdGpu::Buffer buf1 = desc1.GetSharp(info); + const u32 buf1_bpp = AmdGpu::NumBitsPerBlock(buf1.GetDataFmt()); + if (buf0.GetSize() != 16 || (cs_pgm.dim_x * 128ULL * (buf1_bpp / 8)) != buf1.GetSize()) { + return false; + } + + // Find image the buffer alias + const auto image1_id = + texture_cache.FindImageFromRange(buf1.base_address, buf1.GetSize(), false); + if (!image1_id) { + return false; + } + + // Image clear must be valid + VideoCore::Image& image1 = texture_cache.GetImage(image1_id); + if (image1.info.guest_size != buf1.GetSize() || image1.info.num_bits != buf1_bpp || + image1.info.props.is_depth) { + return false; + } + + // Perform image clear + const float* values = reinterpret_cast(buf0.base_address); + const vk::ClearValue clear = { + .color = {.float32 = std::array{values[0], values[1], values[2], values[3]}}, + }; + const VideoCore::SubresourceRange range = { + .base = + { + .level = 0, + .layer = 0, + }, + .extent = image1.info.resources, + }; + image1.Clear(clear, range); + image1.flags |= VideoCore::ImageFlagBits::GpuModified; + image1.flags &= ~VideoCore::ImageFlagBits::Dirty; + return true; +} + void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Bindings& binding, Shader::PushData& push_data) { buffer_bindings.clear(); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 524a8f06d..96a3c95e8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -112,6 +112,7 @@ private: bool IsComputeMetaClear(const Pipeline* pipeline); bool IsComputeImageCopy(const Pipeline* pipeline); + bool IsComputeImageClear(const Pipeline* pipeline); private: friend class VideoCore::BufferCache; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 78286957f..da7467dfb 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -84,15 +84,6 @@ void Scheduler::Wait(u64 tick) { Flush(info); } master_semaphore.Wait(tick); - - // CAUTION: This can introduce unexpected variation in the wait time. - // We don't currently sync the GPU, and some games are very sensitive to this. - // If this becomes a problem, it can be commented out. - // Idealy we would implement proper gpu sync. - while (!pending_ops.empty() && pending_ops.front().gpu_tick <= tick) { - pending_ops.front().callback(); - pending_ops.pop(); - } } void Scheduler::PopPendingOperations() { @@ -109,9 +100,7 @@ void Scheduler::AllocateWorkerCommandBuffers() { }; current_cmdbuf = command_pool.Commit(); - auto begin_result = current_cmdbuf.begin(begin_info); - ASSERT_MSG(begin_result == vk::Result::eSuccess, "Failed to begin command buffer: {}", - vk::to_string(begin_result)); + Check(current_cmdbuf.begin(begin_info)); // Invalidate dynamic state so it gets applied to the new command buffer. dynamic_state.Invalidate(); @@ -139,9 +128,7 @@ void Scheduler::SubmitExecution(SubmitInfo& info) { #endif EndRendering(); - auto end_result = current_cmdbuf.end(); - ASSERT_MSG(end_result == vk::Result::eSuccess, "Failed to end command buffer: {}", - vk::to_string(end_result)); + Check(current_cmdbuf.end()); const vk::Semaphore timeline = master_semaphore.Handle(); info.AddSignal(timeline, signal_value);