diff --git a/.github/linux-appimage-qt.sh b/.github/linux-appimage-qt.sh index fe77c678c..06d5cbc11 100755 --- a/.github/linux-appimage-qt.sh +++ b/.github/linux-appimage-qt.sh @@ -9,6 +9,8 @@ fi export Qt6_DIR="/usr/lib/qt6" export PATH="$Qt6_DIR/bin:$PATH" +export EXTRA_QT_PLUGINS="waylandcompositor" +export EXTRA_PLATFORM_PLUGINS="libqwayland-egl.so;libqwayland-generic.so" # Prepare Tools for building the AppImage wget -q https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7950084cd..ee09163fd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -287,7 +287,7 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential libasound2-dev libpulse-dev libopenal-dev - name: Cache CMake Configuration uses: actions/cache@v4 @@ -343,7 +343,7 @@ jobs: submodules: recursive - name: Install dependencies - run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev + run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 clang build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev - name: Cache CMake Configuration uses: actions/cache@v4 diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100644 new mode 100755 index 2db263b3a..37021746d --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -282,6 +282,14 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp src/core/libraries/audio3d/audio3d_error.h src/core/libraries/audio3d/audio3d_impl.cpp src/core/libraries/audio3d/audio3d_impl.h + src/core/libraries/ime/ime.cpp + src/core/libraries/ime/ime.h + src/core/libraries/game_live_streaming/gamelivestreaming.cpp + src/core/libraries/game_live_streaming/gamelivestreaming.h + src/core/libraries/remote_play/remoteplay.cpp + src/core/libraries/remote_play/remoteplay.h + src/core/libraries/share_play/shareplay.cpp + src/core/libraries/share_play/shareplay.h ) set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h @@ -297,6 +305,8 @@ set(LIBC_SOURCES src/core/libraries/libc_internal/libc_internal.cpp set(DIALOGS_LIB src/core/libraries/dialogs/error_dialog.cpp src/core/libraries/dialogs/error_dialog.h + src/core/libraries/dialogs/ime_dialog_ui.cpp + src/core/libraries/dialogs/ime_dialog_ui.h src/core/libraries/dialogs/ime_dialog.cpp src/core/libraries/dialogs/ime_dialog.h src/core/libraries/dialogs/error_codes.h @@ -324,6 +334,10 @@ set(USBD_LIB src/core/libraries/usbd/usbd.cpp src/core/libraries/usbd/usbd.h ) +set(FIBER_LIB src/core/libraries/fiber/fiber.cpp + src/core/libraries/fiber/fiber.h +) + set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp src/core/libraries/np_manager/np_manager.h src/core/libraries/np_score/np_score.cpp @@ -464,6 +478,7 @@ set(CORE src/core/aerolib/stubs.cpp ${USBD_LIB} ${MISC_LIBS} ${DIALOGS_LIB} + ${FIBER_LIB} ${DEV_TOOLS} src/core/debug_state.cpp src/core/debug_state.h @@ -693,6 +708,8 @@ set(QT_GUI src/qt_gui/about_dialog.cpp src/qt_gui/game_grid_frame.h src/qt_gui/game_install_dialog.cpp src/qt_gui/game_install_dialog.h + src/qt_gui/install_dir_select.cpp + src/qt_gui/install_dir_select.h src/qt_gui/pkg_viewer.cpp src/qt_gui/pkg_viewer.h src/qt_gui/trophy_viewer.cpp @@ -863,3 +880,11 @@ endif() # Discord RPC target_link_libraries(shadps4 PRIVATE discord-rpc) + +# Install rules +install(TARGETS shadps4 BUNDLE DESTINATION .) + +if (ENABLE_QT_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux") + install(FILES ".github/shadps4.desktop" DESTINATION "share/applications") + install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps") +endif() diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index a528eaedb..2f9336c21 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -3,7 +3,10 @@ set(BUILD_SHARED_LIBS OFF) set(BUILD_TESTING OFF) -set_property(DIRECTORY PROPERTY EXCLUDE_FROM_ALL ON) +set_directory_properties(PROPERTIES + EXCLUDE_FROM_ALL ON + SYSTEM ON +) if (MSVC) # Silence "deprecation" warnings @@ -13,11 +16,8 @@ endif() # Boost if (NOT TARGET Boost::headers) set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/ext-boost" CACHE STRING "") - set(Boost_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/externals/ext-boost" CACHE STRING "") set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "") - add_library(boost INTERFACE) - target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR}) - add_library(Boost::headers ALIAS boost) + add_subdirectory(ext-boost) endif() # fmtlib @@ -77,7 +77,7 @@ endif() # RenderDoc if (NOT TARGET RenderDoc::API) add_library(renderdoc INTERFACE) - target_include_directories(renderdoc SYSTEM INTERFACE ./renderdoc) + target_include_directories(renderdoc INTERFACE ./renderdoc) add_library(RenderDoc::API ALIAS renderdoc) endif() @@ -190,4 +190,4 @@ add_subdirectory(discord-rpc/) target_include_directories(discord-rpc INTERFACE discord-rpc/include) # GCN Headers -add_subdirectory(gcn) \ No newline at end of file +add_subdirectory(gcn) diff --git a/externals/ext-boost b/externals/ext-boost index a04136add..f2474e1b5 160000 --- a/externals/ext-boost +++ b/externals/ext-boost @@ -1 +1 @@ -Subproject commit a04136add1e469f46d8ae8d3e8307779240a5c53 +Subproject commit f2474e1b584fb7a3ed6f85ba875e6eacd742ec8a diff --git a/src/common/config.cpp b/src/common/config.cpp index ef82819a3..1974bc6ed 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -63,7 +63,7 @@ static s16 cursorState = HideCursorState::Idle; static int cursorHideTimeout = 5; // 5 seconds (default) // Gui -std::filesystem::path settings_install_dir = {}; +std::vector settings_install_dirs = {}; std::filesystem::path settings_addon_install_dir = {}; u32 main_window_geometry_x = 400; u32 main_window_geometry_y = 400; @@ -334,8 +334,19 @@ void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) { main_window_geometry_w = w; main_window_geometry_h = h; } -void setGameInstallDir(const std::filesystem::path& dir) { - settings_install_dir = dir; +bool addGameInstallDir(const std::filesystem::path& dir) { + if (std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir) == + settings_install_dirs.end()) { + settings_install_dirs.push_back(dir); + return true; + } + return false; +} +void removeGameInstallDir(const std::filesystem::path& dir) { + auto iterator = std::find(settings_install_dirs.begin(), settings_install_dirs.end(), dir); + if (iterator != settings_install_dirs.end()) { + settings_install_dirs.erase(iterator); + } } void setAddonInstallDir(const std::filesystem::path& dir) { settings_addon_install_dir = dir; @@ -393,8 +404,8 @@ u32 getMainWindowGeometryW() { u32 getMainWindowGeometryH() { return main_window_geometry_h; } -std::filesystem::path getGameInstallDir() { - return settings_install_dir; +const std::vector& getGameInstallDirs() { + return settings_install_dirs; } std::filesystem::path getAddonInstallDir() { if (settings_addon_install_dir.empty()) { @@ -490,6 +501,7 @@ void load(const std::filesystem::path& path) { cursorState = toml::find_or(input, "cursorState", HideCursorState::Idle); cursorHideTimeout = toml::find_or(input, "cursorHideTimeout", 5); + backButtonBehavior = toml::find_or(input, "backButtonBehavior", "left"); useSpecialPad = toml::find_or(input, "useSpecialPad", false); specialPadClass = toml::find_or(input, "specialPadClass", 1); } @@ -533,7 +545,19 @@ void load(const std::filesystem::path& path) { mw_themes = toml::find_or(gui, "theme", 0); m_window_size_W = toml::find_or(gui, "mw_width", 0); m_window_size_H = toml::find_or(gui, "mw_height", 0); - settings_install_dir = toml::find_fs_path_or(gui, "installDir", {}); + + // TODO Migration code, after a major release this should be removed. + auto old_game_install_dir = toml::find_fs_path_or(gui, "installDir", {}); + if (!old_game_install_dir.empty()) { + settings_install_dirs.emplace_back(std::filesystem::path{old_game_install_dir}); + } else { + const auto install_dir_array = + toml::find_or>(gui, "installDirs", {}); + for (const auto& dir : install_dir_array) { + settings_install_dirs.emplace_back(std::filesystem::path{dir}); + } + } + settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {}); main_window_geometry_x = toml::find_or(gui, "geometry_x", 0); main_window_geometry_y = toml::find_or(gui, "geometry_y", 0); @@ -599,7 +623,7 @@ void save(const std::filesystem::path& path) { data["General"]["patchFile"] = patchFile; data["Input"]["cursorState"] = cursorState; data["Input"]["cursorHideTimeout"] = cursorHideTimeout; - data["General"]["backButtonBehavior"] = backButtonBehavior; + data["Input"]["backButtonBehavior"] = backButtonBehavior; data["Input"]["useSpecialPad"] = useSpecialPad; data["Input"]["specialPadClass"] = specialPadClass; data["GPU"]["screenWidth"] = screenWidth; @@ -624,7 +648,13 @@ void save(const std::filesystem::path& path) { data["GUI"]["gameTableMode"] = m_table_mode; data["GUI"]["mw_width"] = m_window_size_W; data["GUI"]["mw_height"] = m_window_size_H; - data["GUI"]["installDir"] = std::string{fmt::UTF(settings_install_dir.u8string()).data}; + + std::vector install_dirs; + for (const auto& dirString : settings_install_dirs) { + install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data}); + } + data["GUI"]["installDirs"] = install_dirs; + data["GUI"]["addonInstallDir"] = std::string{fmt::UTF(settings_addon_install_dir.u8string()).data}; data["GUI"]["geometry_x"] = main_window_geometry_x; @@ -638,6 +668,9 @@ void save(const std::filesystem::path& path) { data["Settings"]["consoleLanguage"] = m_language; + // TODO Migration code, after a major release this should be removed. + data.at("GUI").as_table().erase("installDir"); + std::ofstream file(path, std::ios::out); file << data; file.close(); @@ -649,8 +682,6 @@ void setDefaultValues() { playBGM = false; BGMvolume = 50; enableDiscordRPC = true; - cursorState = HideCursorState::Idle; - cursorHideTimeout = 5; screenWidth = 1280; screenHeight = 720; logFilter = ""; @@ -662,6 +693,8 @@ void setDefaultValues() { updateChannel = "Nightly"; } patchFile = ""; + cursorState = HideCursorState::Idle; + cursorHideTimeout = 5; backButtonBehavior = "left"; useSpecialPad = false; specialPadClass = 1; diff --git a/src/common/config.h b/src/common/config.h index 14537c239..d08a85097 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -21,9 +21,6 @@ bool getPlayBGM(); int getBGMvolume(); bool getEnableDiscordRPC(); -s16 getCursorState(); -int getCursorHideTimeout(); - std::string getLogFilter(); std::string getLogType(); std::string getUserName(); @@ -31,6 +28,9 @@ std::string getUpdateChannel(); std::string getPatchFile(); std::string getBackButtonBehavior(); +s16 getCursorState(); +int getCursorHideTimeout(); +std::string getBackButtonBehavior(); bool getUseSpecialPad(); int getSpecialPadClass(); @@ -61,8 +61,6 @@ void setFullscreenMode(bool enable); void setPlayBGM(bool enable); void setBGMvolume(int volume); void setEnableDiscordRPC(bool enable); -void setCursorState(s16 cursorState); -void setCursorHideTimeout(int newcursorHideTimeout); void setLanguage(u32 language); void setNeoMode(bool enable); void setUserName(const std::string& type); @@ -70,6 +68,9 @@ void setUpdateChannel(const std::string& type); void setPatchFile(const std::string& fileName); void setBackButtonBehavior(const std::string& type); +void setCursorState(s16 cursorState); +void setCursorHideTimeout(int newcursorHideTimeout); +void setBackButtonBehavior(const std::string& type); void setUseSpecialPad(bool use); void setSpecialPadClass(int type); @@ -88,7 +89,8 @@ bool vkCrashDiagnosticEnabled(); // Gui void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h); -void setGameInstallDir(const std::filesystem::path& dir); +bool addGameInstallDir(const std::filesystem::path& dir); +void removeGameInstallDir(const std::filesystem::path& dir); void setAddonInstallDir(const std::filesystem::path& dir); void setMainWindowTheme(u32 theme); void setIconSize(u32 size); @@ -107,7 +109,7 @@ u32 getMainWindowGeometryX(); u32 getMainWindowGeometryY(); u32 getMainWindowGeometryW(); u32 getMainWindowGeometryH(); -std::filesystem::path getGameInstallDir(); +const std::vector& getGameInstallDirs(); std::filesystem::path getAddonInstallDir(); u32 getMainWindowTheme(); u32 getIconSize(); diff --git a/src/common/cstring.h b/src/common/cstring.h index fb29443ee..45c291c14 100644 --- a/src/common/cstring.h +++ b/src/common/cstring.h @@ -81,34 +81,42 @@ public: return std::basic_string_view{data}; } - char* begin() { + T* begin() { if (this == nullptr) { return nullptr; } return data; } - const char* begin() const { + const T* begin() const { if (this == nullptr) { return nullptr; } return data; } - char* end() { + T* end() { if (this == nullptr) { return nullptr; } return data + N; } - const char* end() const { + const T* end() const { if (this == nullptr) { return nullptr; } return data + N; } + constexpr std::size_t capacity() const { + return N; + } + + std::size_t size() const { + return std::char_traits::length(data); + } + T& operator[](size_t idx) { return data[idx]; } @@ -152,6 +160,12 @@ public: static_assert(sizeof(CString<13>) == sizeof(char[13])); // Ensure size still matches a simple array static_assert(std::weakly_incrementable::Iterator>); +template +using CWString = CString; + +template +using CU16String = CString; + #pragma clang diagnostic pop } // namespace Common \ No newline at end of file diff --git a/src/common/io_file.cpp b/src/common/io_file.cpp index 1b28d2bba..dd3a40cae 100644 --- a/src/common/io_file.cpp +++ b/src/common/io_file.cpp @@ -231,7 +231,7 @@ void IOFile::Unlink() { // Mark the file for deletion // TODO: Also remove the file path? -#if _WIN64 +#ifdef _WIN64 FILE_DISPOSITION_INFORMATION disposition; IO_STATUS_BLOCK iosb; @@ -242,7 +242,11 @@ void IOFile::Unlink() { NtSetInformationFile(hfile, &iosb, &disposition, sizeof(disposition), FileDispositionInformation); #else - UNREACHABLE_MSG("Missing Linux implementation"); + if (unlink(file_path.c_str()) != 0) { + const auto ec = std::error_code{errno, std::generic_category()}; + LOG_ERROR(Common_Filesystem, "Failed to unlink the file at path={}, ec_message={}", + PathToUTF8String(file_path), ec.message()); + } #endif } diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index c3088f926..051bbd79e 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -114,6 +114,11 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, AvPlayer) \ SUB(Lib, Ngs2) \ SUB(Lib, Audio3d) \ + SUB(Lib, Ime) \ + SUB(Lib, GameLiveStreaming) \ + SUB(Lib, Remoteplay) \ + SUB(Lib, SharePlay) \ + SUB(Lib, Fiber) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index 749568da1..39319d0dc 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -29,67 +29,72 @@ enum class Level : u8 { * filter.cpp. */ enum class Class : u8 { - Log, ///< Messages about the log system itself - Common, ///< Library routines - Common_Filesystem, ///< Filesystem interface library - Common_Memory, ///< Memory mapping and management functions - Core, ///< LLE emulation core - Core_Linker, ///< The module linker - Config, ///< Emulator configuration (including commandline) - Debug, ///< Debugging tools - Kernel, ///< The HLE implementation of the PS4 kernel. - Kernel_Pthread, ///< The pthread implementation of the kernel. - Kernel_Fs, ///< The filesystem implementation of the kernel. - Kernel_Vmm, ///< The virtual memory implementation of the kernel. - Kernel_Event, ///< The event management implementation of the kernel. - Kernel_Sce, ///< The sony specific interfaces provided by the kernel. - Lib, ///< HLE implementation of system library. Each major library - ///< should have its own subclass. - Lib_LibC, ///< The LibC implementation. - Lib_Kernel, ///< The LibKernel implementation. - Lib_Pad, ///< The LibScePad implementation. - Lib_GnmDriver, ///< The LibSceGnmDriver implementation. - Lib_SystemService, ///< The LibSceSystemService implementation. - Lib_UserService, ///< The LibSceUserService implementation. - Lib_VideoOut, ///< The LibSceVideoOut implementation. - Lib_CommonDlg, ///< The LibSceCommonDialog implementation. - Lib_MsgDlg, ///< The LibSceMsgDialog implementation. - Lib_AudioOut, ///< The LibSceAudioOut implementation. - Lib_AudioIn, ///< The LibSceAudioIn implementation. - Lib_Net, ///< The LibSceNet implementation. - Lib_NetCtl, ///< The LibSecNetCtl implementation. - Lib_SaveData, ///< The LibSceSaveData implementation. - Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation. - Lib_Ssl, ///< The LibSceSsl implementation. - Lib_Http, ///< The LibSceHttp implementation. - Lib_SysModule, ///< The LibSceSysModule implementation - Lib_NpManager, ///< The LibSceNpManager implementation - Lib_NpScore, ///< The LibSceNpScore implementation - Lib_NpTrophy, ///< The LibSceNpTrophy implementation - Lib_Screenshot, ///< The LibSceScreenshot implementation - Lib_LibCInternal, ///< The LibCInternal implementation. - Lib_AppContent, ///< The LibSceAppContent implementation. - Lib_Rtc, ///< The LibSceRtc implementation. - Lib_DiscMap, ///< The LibSceDiscMap implementation. - Lib_Png, ///< The LibScePng implementation. - Lib_PlayGo, ///< The LibScePlayGo implementation. - Lib_Random, ///< The libSceRandom implementation. - Lib_Usbd, ///< The LibSceUsbd implementation. - Lib_Ajm, ///< The LibSceAjm implementation. - Lib_ErrorDialog, ///< The LibSceErrorDialog implementation. - Lib_ImeDialog, ///< The LibSceImeDialog implementation. - Lib_AvPlayer, ///< The LibSceAvPlayer implementation. - Lib_Ngs2, ///< The LibSceNgs2 implementation. - Lib_Audio3d, ///< The LibSceAudio3d implementation. - Frontend, ///< Emulator UI - Render, ///< Video Core - Render_Vulkan, ///< Vulkan backend - Render_Recompiler, ///< Shader recompiler - ImGui, ///< ImGui - Loader, ///< ROM loader - Input, ///< Input emulation - Tty, ///< Debug output from emu - Count ///< Total number of logging classes + Log, ///< Messages about the log system itself + Common, ///< Library routines + Common_Filesystem, ///< Filesystem interface library + Common_Memory, ///< Memory mapping and management functions + Core, ///< LLE emulation core + Core_Linker, ///< The module linker + Config, ///< Emulator configuration (including commandline) + Debug, ///< Debugging tools + Kernel, ///< The HLE implementation of the PS4 kernel. + Kernel_Pthread, ///< The pthread implementation of the kernel. + Kernel_Fs, ///< The filesystem implementation of the kernel. + Kernel_Vmm, ///< The virtual memory implementation of the kernel. + Kernel_Event, ///< The event management implementation of the kernel. + Kernel_Sce, ///< The sony specific interfaces provided by the kernel. + Lib, ///< HLE implementation of system library. Each major library + ///< should have its own subclass. + Lib_LibC, ///< The LibC implementation. + Lib_Kernel, ///< The LibKernel implementation. + Lib_Pad, ///< The LibScePad implementation. + Lib_GnmDriver, ///< The LibSceGnmDriver implementation. + Lib_SystemService, ///< The LibSceSystemService implementation. + Lib_UserService, ///< The LibSceUserService implementation. + Lib_VideoOut, ///< The LibSceVideoOut implementation. + Lib_CommonDlg, ///< The LibSceCommonDialog implementation. + Lib_MsgDlg, ///< The LibSceMsgDialog implementation. + Lib_AudioOut, ///< The LibSceAudioOut implementation. + Lib_AudioIn, ///< The LibSceAudioIn implementation. + Lib_Net, ///< The LibSceNet implementation. + Lib_NetCtl, ///< The LibSecNetCtl implementation. + Lib_SaveData, ///< The LibSceSaveData implementation. + Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation. + Lib_Ssl, ///< The LibSceSsl implementation. + Lib_Http, ///< The LibSceHttp implementation. + Lib_SysModule, ///< The LibSceSysModule implementation + Lib_NpManager, ///< The LibSceNpManager implementation + Lib_NpScore, ///< The LibSceNpScore implementation + Lib_NpTrophy, ///< The LibSceNpTrophy implementation + Lib_Screenshot, ///< The LibSceScreenshot implementation + Lib_LibCInternal, ///< The LibCInternal implementation. + Lib_AppContent, ///< The LibSceAppContent implementation. + Lib_Rtc, ///< The LibSceRtc implementation. + Lib_DiscMap, ///< The LibSceDiscMap implementation. + Lib_Png, ///< The LibScePng implementation. + Lib_PlayGo, ///< The LibScePlayGo implementation. + Lib_Random, ///< The libSceRandom implementation. + Lib_Usbd, ///< The LibSceUsbd implementation. + Lib_Ajm, ///< The LibSceAjm implementation. + Lib_ErrorDialog, ///< The LibSceErrorDialog implementation. + Lib_ImeDialog, ///< The LibSceImeDialog implementation. + Lib_AvPlayer, ///< The LibSceAvPlayer implementation. + Lib_Ngs2, ///< The LibSceNgs2 implementation. + Lib_Audio3d, ///< The LibSceAudio3d implementation. + Lib_Ime, ///< The LibSceIme implementation + Lib_GameLiveStreaming, ///< The LibSceGameLiveStreaming implementation + Lib_Remoteplay, ///< The LibSceRemotePlay implementation + Lib_SharePlay, ///< The LibSceSharePlay implemenation + Lib_Fiber, ///< The LibSceFiber implementation. + Frontend, ///< Emulator UI + Render, ///< Video Core + Render_Vulkan, ///< Vulkan backend + Render_Recompiler, ///< Shader recompiler + ImGui, ///< ImGui + Loader, ///< ROM loader + Input, ///< Input emulation + Tty, ///< Debug output from emu + Count ///< Total number of logging classes }; } // namespace Common::Log diff --git a/src/common/path_util.cpp b/src/common/path_util.cpp index 27098e2d1..7551d3b05 100644 --- a/src/common/path_util.cpp +++ b/src/common/path_util.cpp @@ -95,6 +95,18 @@ static auto UserPaths = [] { user_dir = std::filesystem::path(getenv("HOME")) / "Library" / "Application Support" / "shadPS4"; } +#elif defined(__linux__) + auto user_dir = std::filesystem::current_path() / PORTABLE_DIR; + // Check if the "user" directory exists in the current path: + if (!std::filesystem::exists(user_dir)) { + // If it doesn't exist, use XDG_DATA_HOME if it is set, and provide a standard default + const char* xdg_data_home = getenv("XDG_DATA_HOME"); + if (xdg_data_home != nullptr && strlen(xdg_data_home) > 0) { + user_dir = std::filesystem::path(xdg_data_home) / "shadPS4"; + } else { + user_dir = std::filesystem::path(getenv("HOME")) / ".local" / "share" / "shadPS4"; + } + } #else const auto user_dir = std::filesystem::current_path() / PORTABLE_DIR; #endif diff --git a/src/core/libraries/avplayer/avplayer_impl.cpp b/src/core/libraries/avplayer/avplayer_impl.cpp index 6de7b4c20..d193e765f 100644 --- a/src/core/libraries/avplayer/avplayer_impl.cpp +++ b/src/core/libraries/avplayer/avplayer_impl.cpp @@ -6,8 +6,10 @@ #include "avplayer_impl.h" #include "common/logging/log.h" +#include "common/singleton.h" #include "core/libraries/error_codes.h" #include "core/libraries/kernel/libkernel.h" +#include "core/linker.h" using namespace Libraries::Kernel; @@ -17,28 +19,32 @@ void* PS4_SYSV_ABI AvPlayer::Allocate(void* handle, u32 alignment, u32 size) { const auto* const self = reinterpret_cast(handle); const auto allocate = self->m_init_data_original.memory_replacement.allocate; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return allocate(ptr, alignment, size); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(allocate, ptr, alignment, size); } void PS4_SYSV_ABI AvPlayer::Deallocate(void* handle, void* memory) { const auto* const self = reinterpret_cast(handle); const auto deallocate = self->m_init_data_original.memory_replacement.deallocate; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return deallocate(ptr, memory); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(deallocate, ptr, memory); } void* PS4_SYSV_ABI AvPlayer::AllocateTexture(void* handle, u32 alignment, u32 size) { const auto* const self = reinterpret_cast(handle); const auto allocate = self->m_init_data_original.memory_replacement.allocate_texture; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return allocate(ptr, alignment, size); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(allocate, ptr, alignment, size); } void PS4_SYSV_ABI AvPlayer::DeallocateTexture(void* handle, void* memory) { const auto* const self = reinterpret_cast(handle); const auto deallocate = self->m_init_data_original.memory_replacement.deallocate_texture; const auto ptr = self->m_init_data_original.memory_replacement.object_ptr; - return deallocate(ptr, memory); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(deallocate, ptr, memory); } int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) { @@ -47,7 +53,8 @@ int PS4_SYSV_ABI AvPlayer::OpenFile(void* handle, const char* filename) { const auto open = self->m_init_data_original.file_replacement.open; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return open(ptr, filename); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(open, ptr, filename); } int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) { @@ -56,7 +63,8 @@ int PS4_SYSV_ABI AvPlayer::CloseFile(void* handle) { const auto close = self->m_init_data_original.file_replacement.close; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return close(ptr); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(close, ptr); } int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position, u32 length) { @@ -65,7 +73,8 @@ int PS4_SYSV_ABI AvPlayer::ReadOffsetFile(void* handle, u8* buffer, u64 position const auto read_offset = self->m_init_data_original.file_replacement.readOffset; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return read_offset(ptr, buffer, position, length); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(read_offset, ptr, buffer, position, length); } u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { @@ -74,7 +83,8 @@ u64 PS4_SYSV_ABI AvPlayer::SizeFile(void* handle) { const auto size = self->m_init_data_original.file_replacement.size; const auto ptr = self->m_init_data_original.file_replacement.object_ptr; - return size(ptr); + const auto* linker = Common::Singleton::Instance(); + return linker->ExecuteGuest(size, ptr); } SceAvPlayerInitData AvPlayer::StubInitData(const SceAvPlayerInitData& data) { diff --git a/src/core/libraries/avplayer/avplayer_state.cpp b/src/core/libraries/avplayer/avplayer_state.cpp index b0e498479..5cabdba2c 100644 --- a/src/core/libraries/avplayer/avplayer_state.cpp +++ b/src/core/libraries/avplayer/avplayer_state.cpp @@ -87,7 +87,12 @@ void PS4_SYSV_ABI AvPlayerState::AutoPlayEventCallback(void* opaque, SceAvPlayer return; } - // Pass other events to the game + DefaultEventCallback(opaque, event_id, 0, event_data); +} + +void AvPlayerState::DefaultEventCallback(void* opaque, SceAvPlayerEvents event_id, s32 source_id, + void* event_data) { + auto const self = reinterpret_cast(opaque); const auto callback = self->m_event_replacement.event_callback; const auto ptr = self->m_event_replacement.object_ptr; if (callback != nullptr) { @@ -102,8 +107,10 @@ AvPlayerState::AvPlayerState(const SceAvPlayerInitData& init_data) if (m_event_replacement.event_callback == nullptr || init_data.auto_start) { m_auto_start = true; m_init_data.event_replacement.event_callback = &AvPlayerState::AutoPlayEventCallback; - m_init_data.event_replacement.object_ptr = this; + } else { + m_init_data.event_replacement.event_callback = &AvPlayerState::DefaultEventCallback; } + m_init_data.event_replacement.object_ptr = this; if (init_data.default_language != nullptr) { std::memcpy(m_default_language, init_data.default_language, sizeof(m_default_language)); } @@ -367,8 +374,7 @@ void AvPlayerState::EmitEvent(SceAvPlayerEvents event_id, void* event_data) { const auto callback = m_init_data.event_replacement.event_callback; if (callback) { const auto ptr = m_init_data.event_replacement.object_ptr; - const auto* linker = Common::Singleton::Instance(); - linker->ExecuteGuest(callback, ptr, event_id, 0, event_data); + callback(ptr, event_id, 0, event_data); } } diff --git a/src/core/libraries/avplayer/avplayer_state.h b/src/core/libraries/avplayer/avplayer_state.h index f50d1bc1f..151eea52c 100644 --- a/src/core/libraries/avplayer/avplayer_state.h +++ b/src/core/libraries/avplayer/avplayer_state.h @@ -42,6 +42,9 @@ private: static void PS4_SYSV_ABI AutoPlayEventCallback(void* handle, SceAvPlayerEvents event_id, s32 source_id, void* event_data); + static void PS4_SYSV_ABI DefaultEventCallback(void* handle, SceAvPlayerEvents event_id, + s32 source_id, void* event_data); + void OnWarning(u32 id) override; void OnError() override; void OnEOF() override; diff --git a/src/core/libraries/dialogs/ime_dialog.cpp b/src/core/libraries/dialogs/ime_dialog.cpp index ddb1a89f8..3d168bb79 100644 --- a/src/core/libraries/dialogs/ime_dialog.cpp +++ b/src/core/libraries/dialogs/ime_dialog.cpp @@ -1,28 +1,75 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" #include "ime_dialog.h" +#include "ime_dialog_ui.h" + +static constexpr std::array MAX_X_POSITIONS = {3840.0f, 1920.0f}; +static constexpr std::array MAX_Y_POSITIONS = {2160.0f, 1080.0f}; namespace Libraries::ImeDialog { -static OrbisImeDialogStatus g_ime_dlg_status = OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_NONE; +static OrbisImeDialogStatus g_ime_dlg_status = OrbisImeDialogStatus::NONE; +static OrbisImeDialogResult g_ime_dlg_result{}; +static ImeDialogState g_ime_dlg_state{}; +static ImeDialogUi g_ime_dlg_ui; -int PS4_SYSV_ABI sceImeDialogAbort() { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - return ORBIS_OK; +static bool IsValidOption(OrbisImeDialogOption option, OrbisImeType type) { + if (False(~option & + (OrbisImeDialogOption::MULTILINE | OrbisImeDialogOption::NO_AUTO_COMPLETION))) { + return false; + } + + if (True(option & OrbisImeDialogOption::MULTILINE) && type != OrbisImeType::DEFAULT && + type != OrbisImeType::BASIC_LATIN) { + return false; + } + + if (True(option & OrbisImeDialogOption::NO_AUTO_COMPLETION) && type != OrbisImeType::NUMBER && + type != OrbisImeType::BASIC_LATIN) { + return false; + } + + return true; } -int PS4_SYSV_ABI sceImeDialogForceClose() { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - return ORBIS_OK; +Error PS4_SYSV_ABI sceImeDialogAbort() { + if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); + return Error::DIALOG_NOT_IN_USE; + } + + if (g_ime_dlg_status != OrbisImeDialogStatus::RUNNING) { + LOG_INFO(Lib_ImeDialog, "IME dialog not running"); + return Error::DIALOG_NOT_RUNNING; + } + + g_ime_dlg_status = OrbisImeDialogStatus::FINISHED; + g_ime_dlg_result.endstatus = OrbisImeDialogEndStatus::ABORTED; + + return Error::OK; } -int PS4_SYSV_ABI sceImeDialogForTestFunction() { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - return ORBIS_OK; +Error PS4_SYSV_ABI sceImeDialogForceClose() { + if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); + return Error::DIALOG_NOT_IN_USE; + } + + g_ime_dlg_status = OrbisImeDialogStatus::NONE; + g_ime_dlg_ui = ImeDialogUi(); + g_ime_dlg_state = ImeDialogState(); + + return Error::OK; +} + +Error PS4_SYSV_ABI sceImeDialogForTestFunction() { + return Error::INTERNAL; } int PS4_SYSV_ABI sceImeDialogGetCurrentStarState() { @@ -45,26 +92,118 @@ int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended() { return ORBIS_OK; } -int PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) { - result->endstatus = OrbisImeDialogEndStatus::ORBIS_IME_DIALOG_END_STATUS_OK; - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - return ORBIS_OK; +Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result) { + if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog is not running"); + return Error::DIALOG_NOT_IN_USE; + } + + if (result == nullptr) { + LOG_INFO(Lib_ImeDialog, "called with result (NULL)"); + return Error::INVALID_ADDRESS; + } + + result->endstatus = g_ime_dlg_result.endstatus; + + if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { + return Error::DIALOG_NOT_FINISHED; + } + + g_ime_dlg_state.CopyTextToOrbisBuffer(); + return Error::OK; } -int PS4_SYSV_ABI sceImeDialogGetStatus() { - if (g_ime_dlg_status == OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_RUNNING) { - return OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_FINISHED; +OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus() { + if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { + g_ime_dlg_state.CallTextFilter(); } + return g_ime_dlg_status; } -int PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - const std::wstring_view text = L"shadPS4"; - param->maxTextLength = text.size(); - std::memcpy(param->inputTextBuffer, text.data(), text.size() * sizeof(wchar_t)); - g_ime_dlg_status = OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_RUNNING; - return ORBIS_OK; +Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended) { + if (g_ime_dlg_status != OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog is already running"); + return Error::BUSY; + } + + if (param == nullptr) { + LOG_INFO(Lib_ImeDialog, "called with param (NULL)"); + return Error::INVALID_ADDRESS; + } + + if (!magic_enum::enum_contains(param->type)) { + LOG_INFO(Lib_ImeDialog, "Invalid param->type"); + return Error::INVALID_ADDRESS; + } + + // TODO: do correct param->option validation + // TODO: do correct param->supportedLanguages validation + + if (param->posx < 0.0f || + param->posx >= + MAX_X_POSITIONS[False(param->option & OrbisImeDialogOption::LARGE_RESOLUTION)]) { + LOG_INFO(Lib_ImeDialog, "Invalid param->posx"); + return Error::INVALID_POSX; + } + + if (param->posy < 0.0f || + param->posy >= + MAX_Y_POSITIONS[False(param->option & OrbisImeDialogOption::LARGE_RESOLUTION)]) { + LOG_INFO(Lib_ImeDialog, "Invalid param->posy"); + return Error::INVALID_POSY; + } + + if (!magic_enum::enum_contains(param->horizontalAlignment)) { + LOG_INFO(Lib_ImeDialog, "Invalid param->horizontalAlignment"); + return Error::INVALID_HORIZONTALIGNMENT; + } + + if (!magic_enum::enum_contains(param->verticalAlignment)) { + LOG_INFO(Lib_ImeDialog, "Invalid param->verticalAlignment"); + return Error::INVALID_VERTICALALIGNMENT; + } + + if (!IsValidOption(param->option, param->type)) { + LOG_INFO(Lib_ImeDialog, "Invalid param->option"); + return Error::INVALID_PARAM; + } + + if (param->inputTextBuffer == nullptr) { + LOG_INFO(Lib_ImeDialog, "Invalid param->inputTextBuffer"); + return Error::INVALID_INPUT_TEXT_BUFFER; + } + + if (extended) { + if (magic_enum::enum_contains(extended->priority)) { + LOG_INFO(Lib_ImeDialog, "Invalid extended->priority"); + return Error::INVALID_EXTENDED; + } + + // TODO: do correct extended->option validation + + if ((extended->extKeyboardMode & 0xe3fffffc) != 0) { + LOG_INFO(Lib_ImeDialog, "Invalid extended->extKeyboardMode"); + return Error::INVALID_EXTENDED; + } + + if (extended->disableDevice > 7) { + LOG_INFO(Lib_ImeDialog, "Invalid extended->disableDevice"); + return Error::INVALID_EXTENDED; + } + } + + if (param->maxTextLength > ORBIS_IME_DIALOG_MAX_TEXT_LENGTH) { + LOG_INFO(Lib_ImeDialog, "Invalid param->maxTextLength"); + return Error::INVALID_MAX_TEXT_LENGTH; + } + + g_ime_dlg_result = {}; + g_ime_dlg_state = ImeDialogState(param, extended); + g_ime_dlg_status = OrbisImeDialogStatus::RUNNING; + g_ime_dlg_ui = ImeDialogUi(&g_ime_dlg_state, &g_ime_dlg_status, &g_ime_dlg_result); + + return Error::OK; } int PS4_SYSV_ABI sceImeDialogInitInternal() { @@ -87,10 +226,22 @@ int PS4_SYSV_ABI sceImeDialogSetPanelPosition() { return ORBIS_OK; } -int PS4_SYSV_ABI sceImeDialogTerm() { - LOG_ERROR(Lib_ImeDialog, "(STUBBED) called"); - g_ime_dlg_status = OrbisImeDialogStatus::ORBIS_IME_DIALOG_STATUS_NONE; - return ORBIS_OK; +Error PS4_SYSV_ABI sceImeDialogTerm() { + if (g_ime_dlg_status == OrbisImeDialogStatus::NONE) { + LOG_INFO(Lib_ImeDialog, "IME dialog not in use"); + return Error::DIALOG_NOT_IN_USE; + } + + if (g_ime_dlg_status == OrbisImeDialogStatus::RUNNING) { + LOG_INFO(Lib_ImeDialog, "IME dialog is still running"); + return Error::DIALOG_NOT_FINISHED; + } + + g_ime_dlg_status = OrbisImeDialogStatus::NONE; + g_ime_dlg_ui = ImeDialogUi(); + g_ime_dlg_state = ImeDialogState(); + + return Error::OK; } void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym) { diff --git a/src/core/libraries/dialogs/ime_dialog.h b/src/core/libraries/dialogs/ime_dialog.h index ffe42b31a..66cf9fb93 100644 --- a/src/core/libraries/dialogs/ime_dialog.h +++ b/src/core/libraries/dialogs/ime_dialog.h @@ -3,6 +3,7 @@ #pragma once +#include "common/enum.h" #include "common/types.h" namespace Core::Loader { @@ -11,71 +12,150 @@ class SymbolsResolver; namespace Libraries::ImeDialog { -enum OrbisImeDialogStatus { - ORBIS_IME_DIALOG_STATUS_NONE = 0, - ORBIS_IME_DIALOG_STATUS_RUNNING = 1, - ORBIS_IME_DIALOG_STATUS_FINISHED = 2 +constexpr u32 ORBIS_IME_DIALOG_MAX_TEXT_LENGTH = 0x78; + +enum class Error : u32 { + OK = 0x0, + BUSY = 0x80bc0001, + NOT_OPENED = 0x80bc0002, + NO_MEMORY = 0x80bc0003, + CONNECTION_FAILED = 0x80bc0004, + TOO_MANY_REQUESTS = 0x80bc0005, + INVALID_TEXT = 0x80bc0006, + EVENT_OVERFLOW = 0x80bc0007, + NOT_ACTIVE = 0x80bc0008, + IME_SUSPENDING = 0x80bc0009, + DEVICE_IN_USE = 0x80bc000a, + INVALID_USER_ID = 0x80bc0010, + INVALID_TYPE = 0x80bc0011, + INVALID_SUPPORTED_LANGUAGES = 0x80bc0012, + INVALID_ENTER_LABEL = 0x80bc0013, + INVALID_INPUT_METHOD = 0x80bc0014, + INVALID_OPTION = 0x80bc0015, + INVALID_MAX_TEXT_LENGTH = 0x80bc0016, + INVALID_INPUT_TEXT_BUFFER = 0x80bc0017, + INVALID_POSX = 0x80bc0018, + INVALID_POSY = 0x80bc0019, + INVALID_HORIZONTALIGNMENT = 0x80bc001a, + INVALID_VERTICALALIGNMENT = 0x80bc001b, + INVALID_EXTENDED = 0x80bc001c, + INVALID_KEYBOARD_TYPE = 0x80bc001d, + INVALID_WORK = 0x80bc0020, + INVALID_ARG = 0x80bc0021, + INVALID_HANDLER = 0x80bc0022, + NO_RESOURCE_ID = 0x80bc0023, + INVALID_MODE = 0x80bc0024, + INVALID_PARAM = 0x80bc0030, + INVALID_ADDRESS = 0x80bc0031, + INVALID_RESERVED = 0x80bc0032, + INVALID_TIMING = 0x80bc0033, + INTERNAL = 0x80bc00ff, + DIALOG_INVALID_TITLE = 0x80bc0101, + DIALOG_NOT_RUNNING = 0x80bc0105, + DIALOG_NOT_FINISHED = 0x80bc0106, + DIALOG_NOT_IN_USE = 0x80bc0107, }; -enum OrbisImeDialogEndStatus { - ORBIS_IME_DIALOG_END_STATUS_OK = 0, - ORBIS_IME_DIALOG_END_STATUS_USER_CANCELED = 1, - ORBIS_IME_DIALOG_END_STATUS_ABORTED = 2 +enum class OrbisImeDialogStatus : u32 { + NONE = 0, + RUNNING = 1, + FINISHED = 2, }; -struct OrbisImeDialogResult { - OrbisImeDialogEndStatus endstatus; - s32 reserved[12]; +enum class OrbisImeDialogEndStatus : u32 { + OK = 0, + USER_CANCELED = 1, + ABORTED = 2, }; -enum OrbisImeType { - ORBIS_IME_TYPE_DEFAULT = 0, - ORBIS_IME_TYPE_BASIC_LATIN = 1, - ORBIS_IME_TYPE_URL = 2, - ORBIS_IME_TYPE_MAIL = 3, - ORBIS_IME_TYPE_NUMBER = 4 +enum class OrbisImeType : u32 { + DEFAULT = 0, + BASIC_LATIN = 1, + URL = 2, + MAIL = 3, + NUMBER = 4, }; -enum OrbisImeEnterLabel { - ORBIS_IME_ENTER_LABEL_DEFAULT = 0, - ORBIS_IME_ENTER_LABEL_SEND = 1, - ORBIS_IME_ENTER_LABEL_SEARCH = 2, - ORBIS_IME_ENTER_LABEL_GO = 3 -}; -enum OrbiImeInputMethod { ORBIS_IME_INPUT_METHOD_DEFAULT = 0 }; - -typedef int (*OrbisImeTextFilter)(wchar_t* outText, u32* outTextLength, const wchar_t* srcText, - u32 srcTextLength); - -enum OrbisImeHorizontalAlignment { - ORBIS_IME_HALIGN_LEFT = 0, - ORBIS_IME_HALIGN_CENTER = 1, - ORBIS_IME_HALIGN_RIGHT = 2 +enum class OrbisImeEnterLabel : u32 { + DEFAULT = 0, + SEND = 1, + SEARCH = 2, + GO = 3, }; -enum OrbisImeVerticalAlignment { - ORBIS_IME_VALIGN_TOP = 0, - ORBIS_IME_VALIGN_CENTER = 1, - ORBIS_IME_VALIGN_BOTTOM = 2 +enum class OrbisImeDialogOption : u32 { + DEFAULT = 0, + MULTILINE = 1, + NO_AUTO_CORRECTION = 2, + NO_AUTO_COMPLETION = 4, + // TODO: Document missing options + LARGE_RESOLUTION = 1024, }; -struct OrbisImeDialogParam { - s32 userId; - OrbisImeType type; - u64 supportedLanguages; - OrbisImeEnterLabel enterLabel; - OrbiImeInputMethod inputMethod; - OrbisImeTextFilter filter; - u32 option; - u32 maxTextLength; - wchar_t* inputTextBuffer; - float posx; - float posy; - OrbisImeHorizontalAlignment horizontalAlignment; - OrbisImeVerticalAlignment verticalAlignment; - const wchar_t* placeholder; - const wchar_t* title; - s8 reserved[16]; +DECLARE_ENUM_FLAG_OPERATORS(OrbisImeDialogOption) + +enum class OrbisImeInputMethod : u32 { + DEFAULT = 0, +}; + +enum class OrbisImeHorizontalAlignment : u32 { + LEFT = 0, + CENTER = 1, + RIGHT = 2, +}; + +enum class OrbisImeVerticalAlignment : u32 { + TOP = 0, + CENTER = 1, + BOTTOM = 2, +}; + +enum class OrbisImePanelPriority : u32 { + DEFAULT = 0, + ALPHABET = 1, + SYMBOL = 2, + ACCENT = 3, +}; + +enum class OrbisImeKeyboardType : u32 { + NONE = 0, + DANISH = 1, + GERMAN = 2, + GERMAN_SW = 3, + ENGLISH_US = 4, + ENGLISH_GB = 5, + SPANISH = 6, + SPANISH_LA = 7, + FINNISH = 8, + FRENCH = 9, + FRENCH_BR = 10, + FRENCH_CA = 11, + FRENCH_SW = 12, + ITALIAN = 13, + DUTCH = 14, + NORWEGIAN = 15, + POLISH = 16, + PORTUGUESE_BR = 17, + PORTUGUESE_PT = 18, + RUSSIAN = 19, + SWEDISH = 20, + TURKISH = 21, + JAPANESE_ROMAN = 22, + JAPANESE_KANA = 23, + KOREAN = 24, + SM_CHINESE = 25, + TR_CHINESE_ZY = 26, + TR_CHINESE_PY_HK = 27, + TR_CHINESE_PY_TW = 28, + TR_CHINESE_CG = 29, + ARABIC_AR = 30, + THAI = 31, + CZECH = 32, + GREEK = 33, + INDONESIAN = 34, + VIETNAMESE = 35, + ROMANIAN = 36, + HUNGARIAN = 37, }; struct OrbisImeColor { @@ -85,57 +165,14 @@ struct OrbisImeColor { u8 a; }; -enum OrbisImePanelPriority { - ORBIS_IME_PANEL_PRIORITY_DEFAULT = 0, - ORBIS_IME_PANEL_PRIORITY_ALPHABET = 1, - ORBIS_IME_PANEL_PRIORITY_SYMBOL = 2, - ORBIS_IME_PANEL_PRIORITY_ACCENT = 3 -}; - -enum OrbisImeKeyboardType { - ORBIS_IME_KEYBOARD_TYPE_NONE = 0, - ORBIS_IME_KEYBOARD_TYPE_DANISH = 1, - ORBIS_IME_KEYBOARD_TYPE_GERMAN = 2, - ORBIS_IME_KEYBOARD_TYPE_GERMAN_SW = 3, - ORBIS_IME_KEYBOARD_TYPE_ENGLISH_US = 4, - ORBIS_IME_KEYBOARD_TYPE_ENGLISH_GB = 5, - ORBIS_IME_KEYBOARD_TYPE_SPANISH = 6, - ORBIS_IME_KEYBOARD_TYPE_SPANISH_LA = 7, - ORBIS_IME_KEYBOARD_TYPE_FINNISH = 8, - ORBIS_IME_KEYBOARD_TYPE_FRENCH = 9, - ORBIS_IME_KEYBOARD_TYPE_FRENCH_BR = 10, - ORBIS_IME_KEYBOARD_TYPE_FRENCH_CA = 11, - ORBIS_IME_KEYBOARD_TYPE_FRENCH_SW = 12, - ORBIS_IME_KEYBOARD_TYPE_ITALIAN = 13, - ORBIS_IME_KEYBOARD_TYPE_DUTCH = 14, - ORBIS_IME_KEYBOARD_TYPE_NORWEGIAN = 15, - ORBIS_IME_KEYBOARD_TYPE_POLISH = 16, - ORBIS_IME_KEYBOARD_TYPE_PORTUGUESE_BR = 17, - ORBIS_IME_KEYBOARD_TYPE_PORTUGUESE_PT = 18, - ORBIS_IME_KEYBOARD_TYPE_RUSSIAN = 19, - ORBIS_IME_KEYBOARD_TYPE_SWEDISH = 20, - ORBIS_IME_KEYBOARD_TYPE_TURKISH = 21, - ORBIS_IME_KEYBOARD_TYPE_JAPANESE_ROMAN = 22, - ORBIS_IME_KEYBOARD_TYPE_JAPANESE_KANA = 23, - ORBIS_IME_KEYBOARD_TYPE_KOREAN = 24, - ORBIS_IME_KEYBOARD_TYPE_SM_CHINESE = 25, - ORBIS_IME_KEYBOARD_TYPE_TR_CHINESE_ZY = 26, - ORBIS_IME_KEYBOARD_TYPE_TR_CHINESE_PY_HK = 27, - ORBIS_IME_KEYBOARD_TYPE_TR_CHINESE_PY_TW = 28, - ORBIS_IME_KEYBOARD_TYPE_TR_CHINESE_CG = 29, - ORBIS_IME_KEYBOARD_TYPE_ARABIC_AR = 30, - ORBIS_IME_KEYBOARD_TYPE_THAI = 31, - ORBIS_IME_KEYBOARD_TYPE_CZECH = 32, - ORBIS_IME_KEYBOARD_TYPE_GREEK = 33, - ORBIS_IME_KEYBOARD_TYPE_INDONESIAN = 34, - ORBIS_IME_KEYBOARD_TYPE_VIETNAMESE = 35, - ORBIS_IME_KEYBOARD_TYPE_ROMANIAN = 36, - ORBIS_IME_KEYBOARD_TYPE_HUNGARIAN = 37 +struct OrbisImeDialogResult { + OrbisImeDialogEndStatus endstatus; + s32 reserved[12]; }; struct OrbisImeKeycode { u16 keycode; - wchar_t character; + char16_t character; u32 status; OrbisImeKeyboardType type; s32 userId; @@ -143,11 +180,34 @@ struct OrbisImeKeycode { u64 timestamp; }; -typedef int (*OrbisImeExtKeyboardFilter)(const OrbisImeKeycode* srcKeycode, u16* outKeycode, - u32* outStatus, void* reserved); +typedef PS4_SYSV_ABI int (*OrbisImeTextFilter)(char16_t* outText, u32* outTextLength, + const char16_t* srcText, u32 srcTextLength); + +typedef PS4_SYSV_ABI int (*OrbisImeExtKeyboardFilter)(const OrbisImeKeycode* srcKeycode, + u16* outKeycode, u32* outStatus, + void* reserved); + +struct OrbisImeDialogParam { + s32 userId; + OrbisImeType type; + u64 supportedLanguages; + OrbisImeEnterLabel enterLabel; + OrbisImeInputMethod inputMethod; + OrbisImeTextFilter filter; + OrbisImeDialogOption option; + u32 maxTextLength; + char16_t* inputTextBuffer; + float posx; + float posy; + OrbisImeHorizontalAlignment horizontalAlignment; + OrbisImeVerticalAlignment verticalAlignment; + const char16_t* placeholder; + const char16_t* title; + s8 reserved[16]; +}; struct OrbisImeParamExtended { - u32 option; + u32 option; // OrbisImeDialogOptionExtended OrbisImeColor colorBase; OrbisImeColor colorLine; OrbisImeColor colorTextField; @@ -165,21 +225,21 @@ struct OrbisImeParamExtended { int8_t reserved[60]; }; -int PS4_SYSV_ABI sceImeDialogAbort(); -int PS4_SYSV_ABI sceImeDialogForceClose(); -int PS4_SYSV_ABI sceImeDialogForTestFunction(); +Error PS4_SYSV_ABI sceImeDialogAbort(); +Error PS4_SYSV_ABI sceImeDialogForceClose(); +Error PS4_SYSV_ABI sceImeDialogForTestFunction(); int PS4_SYSV_ABI sceImeDialogGetCurrentStarState(); int PS4_SYSV_ABI sceImeDialogGetPanelPositionAndForm(); int PS4_SYSV_ABI sceImeDialogGetPanelSize(); int PS4_SYSV_ABI sceImeDialogGetPanelSizeExtended(); -int PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result); -/*OrbisImeDialogStatus*/ int PS4_SYSV_ABI sceImeDialogGetStatus(); -int PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended); +Error PS4_SYSV_ABI sceImeDialogGetResult(OrbisImeDialogResult* result); +OrbisImeDialogStatus PS4_SYSV_ABI sceImeDialogGetStatus(); +Error PS4_SYSV_ABI sceImeDialogInit(OrbisImeDialogParam* param, OrbisImeParamExtended* extended); int PS4_SYSV_ABI sceImeDialogInitInternal(); int PS4_SYSV_ABI sceImeDialogInitInternal2(); int PS4_SYSV_ABI sceImeDialogInitInternal3(); int PS4_SYSV_ABI sceImeDialogSetPanelPosition(); -int PS4_SYSV_ABI sceImeDialogTerm(); +Error PS4_SYSV_ABI sceImeDialogTerm(); void RegisterlibSceImeDialog(Core::Loader::SymbolsResolver* sym); } // namespace Libraries::ImeDialog \ No newline at end of file diff --git a/src/core/libraries/dialogs/ime_dialog_ui.cpp b/src/core/libraries/dialogs/ime_dialog_ui.cpp new file mode 100644 index 000000000..48f5d75dc --- /dev/null +++ b/src/core/libraries/dialogs/ime_dialog_ui.cpp @@ -0,0 +1,390 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include + +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/singleton.h" +#include "core/libraries/dialogs/ime_dialog.h" +#include "core/libraries/dialogs/ime_dialog_ui.h" +#include "core/linker.h" +#include "imgui/imgui_std.h" + +using namespace ImGui; + +static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f}; + +namespace Libraries::ImeDialog { + +ImeDialogState::ImeDialogState(const OrbisImeDialogParam* param, + const OrbisImeParamExtended* extended) { + if (!param) + return; + + userId = param->userId; + is_multiLine = True(param->option & OrbisImeDialogOption::MULTILINE); + is_numeric = param->type == OrbisImeType::NUMBER; + type = param->type; + enter_label = param->enterLabel; + text_filter = param->filter; + keyboard_filter = extended ? extended->extKeyboardFilter : nullptr; + max_text_length = param->maxTextLength; + text_buffer = param->inputTextBuffer; + + if (param->title) { + std::size_t title_len = std::char_traits::length(param->title); + title.resize(title_len * 4 + 1); + title[title_len * 4] = '\0'; + + if (!ConvertOrbisToUTF8(param->title, title_len, &title[0], title_len * 4)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert title to utf8 encoding"); + } + } + + if (param->placeholder) { + std::size_t placeholder_len = std::char_traits::length(param->placeholder); + placeholder.resize(placeholder_len * 4 + 1); + placeholder[placeholder_len * 4] = '\0'; + + if (!ConvertOrbisToUTF8(param->placeholder, placeholder_len, &placeholder[0], + placeholder_len * 4)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert placeholder to utf8 encoding"); + } + } + + std::size_t text_len = std::char_traits::length(text_buffer); + if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(), + ORBIS_IME_DIALOG_MAX_TEXT_LENGTH * 4)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding"); + } +} + +ImeDialogState::ImeDialogState(ImeDialogState&& other) noexcept + : input_changed(other.input_changed), userId(other.userId), is_multiLine(other.is_multiLine), + is_numeric(other.is_numeric), type(other.type), enter_label(other.enter_label), + text_filter(other.text_filter), keyboard_filter(other.keyboard_filter), + max_text_length(other.max_text_length), text_buffer(other.text_buffer), + title(std::move(other.title)), placeholder(std::move(other.placeholder)), + current_text(other.current_text) { + + other.text_buffer = nullptr; +} + +ImeDialogState& ImeDialogState::operator=(ImeDialogState&& other) { + if (this != &other) { + input_changed = other.input_changed; + userId = other.userId; + is_multiLine = other.is_multiLine; + is_numeric = other.is_numeric; + type = other.type; + enter_label = other.enter_label; + text_filter = other.text_filter; + keyboard_filter = other.keyboard_filter; + max_text_length = other.max_text_length; + text_buffer = other.text_buffer; + title = std::move(other.title); + placeholder = std::move(other.placeholder); + current_text = other.current_text; + + other.text_buffer = nullptr; + } + + return *this; +} + +bool ImeDialogState::CopyTextToOrbisBuffer() { + if (!text_buffer) { + return false; + } + + return ConvertUTF8ToOrbis(current_text.begin(), current_text.capacity(), text_buffer, + max_text_length); +} + +bool ImeDialogState::CallTextFilter() { + if (!text_filter || !input_changed) { + return true; + } + + input_changed = false; + + char16_t src_text[ORBIS_IME_DIALOG_MAX_TEXT_LENGTH + 1] = {0}; + u32 src_text_length = current_text.size(); + char16_t out_text[ORBIS_IME_DIALOG_MAX_TEXT_LENGTH + 1] = {0}; + u32 out_text_length = ORBIS_IME_DIALOG_MAX_TEXT_LENGTH; + + if (!ConvertUTF8ToOrbis(current_text.begin(), src_text_length, src_text, + ORBIS_IME_DIALOG_MAX_TEXT_LENGTH)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert text to orbis encoding"); + return false; + } + + auto* linker = Common::Singleton::Instance(); + int ret = + linker->ExecuteGuest(text_filter, out_text, &out_text_length, src_text, src_text_length); + + if (ret != 0) { + return false; + } + + if (!ConvertOrbisToUTF8(out_text, out_text_length, current_text.begin(), + ORBIS_IME_DIALOG_MAX_TEXT_LENGTH * 4)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding"); + return false; + } + + return true; +} + +bool ImeDialogState::CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* out_keycode, + u32* out_status) { + if (!keyboard_filter) { + return true; + } + + auto* linker = Common::Singleton::Instance(); + int ret = linker->ExecuteGuest(keyboard_filter, src_keycode, out_keycode, out_status, nullptr); + + return ret == 0; +} + +bool ImeDialogState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, + char* utf8_text, std::size_t utf8_text_len) { + + std::fill(utf8_text, utf8_text + utf8_text_len, '\0'); + const ImWchar* orbis_text_ptr = reinterpret_cast(orbis_text); + ImTextStrToUtf8(utf8_text, utf8_text_len, orbis_text_ptr, orbis_text_ptr + orbis_text_len); + + return true; +} + +bool ImeDialogState::ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_text_len, + char16_t* orbis_text, std::size_t orbis_text_len) { + + std::fill(orbis_text, orbis_text + orbis_text_len, u'\0'); + ImTextStrFromUtf8(reinterpret_cast(orbis_text), orbis_text_len, utf8_text, nullptr); + + return true; +} + +ImeDialogUi::ImeDialogUi(ImeDialogState* state, OrbisImeDialogStatus* status, + OrbisImeDialogResult* result) + : state(state), status(status), result(result) { + + if (state && *status == OrbisImeDialogStatus::RUNNING) { + AddLayer(this); + } +} + +ImeDialogUi::~ImeDialogUi() { + std::scoped_lock lock(draw_mutex); + + Free(); +} + +ImeDialogUi::ImeDialogUi(ImeDialogUi&& other) noexcept + : state(other.state), status(other.status), result(other.result), + first_render(other.first_render) { + + std::scoped_lock lock(draw_mutex, other.draw_mutex); + other.state = nullptr; + other.status = nullptr; + other.result = nullptr; + + if (state && *status == OrbisImeDialogStatus::RUNNING) { + AddLayer(this); + } +} + +ImeDialogUi& ImeDialogUi::operator=(ImeDialogUi&& other) { + std::scoped_lock lock(draw_mutex, other.draw_mutex); + Free(); + + state = other.state; + status = other.status; + result = other.result; + first_render = other.first_render; + other.state = nullptr; + other.status = nullptr; + other.result = nullptr; + + if (state && *status == OrbisImeDialogStatus::RUNNING) { + AddLayer(this); + } + + return *this; +} + +void ImeDialogUi::Free() { + RemoveLayer(this); +} + +void ImeDialogUi::Draw() { + std::unique_lock lock{draw_mutex}; + + if (!state) { + return; + } + + if (!status || *status != OrbisImeDialogStatus::RUNNING) { + return; + } + + const auto& ctx = *GetCurrentContext(); + const auto& io = ctx.IO; + + ImVec2 window_size; + + if (state->is_multiLine) { + window_size = {500.0f, 300.0f}; + } else { + window_size = {500.0f, 150.0f}; + } + + CentralizeWindow(); + SetNextWindowSize(window_size); + SetNextWindowCollapsed(false); + + if (first_render || !io.NavActive) { + SetNextWindowFocus(); + } + + if (Begin("IME Dialog##ImeDialog", nullptr, + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoSavedSettings)) { + DrawPrettyBackground(); + + if (!state->title.empty()) { + SetWindowFontScale(1.7f); + TextUnformatted(state->title.data()); + SetWindowFontScale(1.0f); + } + + if (state->is_multiLine) { + DrawMultiLineInputText(); + } else { + DrawInputText(); + } + + SetCursorPosY(GetCursorPosY() + 10.0f); + + const char* button_text; + + switch (state->enter_label) { + case OrbisImeEnterLabel::GO: + button_text = "Go##ImeDialogOK"; + break; + case OrbisImeEnterLabel::SEARCH: + button_text = "Search##ImeDialogOK"; + break; + case OrbisImeEnterLabel::SEND: + button_text = "Send##ImeDialogOK"; + break; + case OrbisImeEnterLabel::DEFAULT: + default: + button_text = "OK##ImeDialogOK"; + break; + } + + float button_spacing = 10.0f; + float total_button_width = BUTTON_SIZE.x * 2 + button_spacing; + float button_start_pos = (window_size.x - total_button_width) / 2.0f; + + SetCursorPosX(button_start_pos); + + if (Button(button_text, BUTTON_SIZE) || + (!state->is_multiLine && IsKeyPressed(ImGuiKey_Enter))) { + *status = OrbisImeDialogStatus::FINISHED; + result->endstatus = OrbisImeDialogEndStatus::OK; + } + + SameLine(0.0f, button_spacing); + + if (Button("Cancel##ImeDialogCancel", BUTTON_SIZE)) { + *status = OrbisImeDialogStatus::FINISHED; + result->endstatus = OrbisImeDialogEndStatus::USER_CANCELED; + } + } + End(); + + first_render = false; +} + +void ImeDialogUi::DrawInputText() { + ImVec2 input_size = {GetWindowWidth() - 40.0f, 0.0f}; + SetCursorPosX(20.0f); + if (first_render) { + SetKeyboardFocusHere(); + } + const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data(); + if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(), + state->max_text_length, input_size, ImGuiInputTextFlags_CallbackCharFilter, + InputTextCallback, this)) { + state->input_changed = true; + } +} + +void ImeDialogUi::DrawMultiLineInputText() { + ImVec2 input_size = {GetWindowWidth() - 40.0f, 200.0f}; + SetCursorPosX(20.0f); + ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackCharFilter | + static_cast(ImGuiInputTextFlags_Multiline); + if (first_render) { + SetKeyboardFocusHere(); + } + const char* placeholder = state->placeholder.empty() ? nullptr : state->placeholder.data(); + if (InputTextEx("##ImeDialogInput", placeholder, state->current_text.begin(), + state->max_text_length, input_size, flags, InputTextCallback, this)) { + state->input_changed = true; + } +} + +int ImeDialogUi::InputTextCallback(ImGuiInputTextCallbackData* data) { + ImeDialogUi* ui = static_cast(data->UserData); + + ASSERT(ui); + + // Should we filter punctuation? + if (ui->state->is_numeric && (data->EventChar < '0' || data->EventChar > '9') && + data->EventChar != '\b' && data->EventChar != ',' && data->EventChar != '.') { + return 1; + } + + if (!ui->state->keyboard_filter) { + return 0; + } + + // ImGui encodes ImWchar32 as multi-byte UTF-8 characters + char* event_char = reinterpret_cast(&data->EventChar); + + // Call the keyboard filter + OrbisImeKeycode src_keycode = { + .keycode = 0, + .character = 0, + .status = 1, // ??? 1 = key pressed, 0 = key released + .type = OrbisImeKeyboardType::ENGLISH_US, // TODO set this to the correct value (maybe use + // the current language?) + .userId = ui->state->userId, + .resourceId = 0, + .timestamp = 0}; + + if (!ui->state->ConvertUTF8ToOrbis(event_char, 4, &src_keycode.character, 1)) { + LOG_ERROR(Lib_ImeDialog, "Failed to convert orbis char to utf8"); + return 0; + } + src_keycode.keycode = src_keycode.character; // TODO set this to the correct value + + u16 out_keycode; + u32 out_status; + + ui->state->CallKeyboardFilter(&src_keycode, &out_keycode, &out_status); + + // TODO. set the keycode + + return 0; +} + +} // namespace Libraries::ImeDialog \ No newline at end of file diff --git a/src/core/libraries/dialogs/ime_dialog_ui.h b/src/core/libraries/dialogs/ime_dialog_ui.h new file mode 100644 index 000000000..96c83954a --- /dev/null +++ b/src/core/libraries/dialogs/ime_dialog_ui.h @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include "common/cstring.h" +#include "common/types.h" +#include "core/libraries/dialogs/ime_dialog.h" +#include "imgui/imgui_layer.h" + +namespace Libraries::ImeDialog { + +class ImeDialogUi; + +class ImeDialogState final { + friend ImeDialogUi; + + bool input_changed = false; + + s32 userId{}; + bool is_multiLine{}; + bool is_numeric{}; + OrbisImeType type{}; + OrbisImeEnterLabel enter_label{}; + OrbisImeTextFilter text_filter{}; + OrbisImeExtKeyboardFilter keyboard_filter{}; + u32 max_text_length{}; + char16_t* text_buffer{}; + std::vector title; + std::vector placeholder; + + // A character can hold up to 4 bytes in UTF-8 + Common::CString current_text; + +public: + ImeDialogState(const OrbisImeDialogParam* param = nullptr, + const OrbisImeParamExtended* extended = nullptr); + ImeDialogState(const ImeDialogState& other) = delete; + ImeDialogState(ImeDialogState&& other) noexcept; + ImeDialogState& operator=(ImeDialogState&& other); + + bool CopyTextToOrbisBuffer(); + bool CallTextFilter(); + +private: + bool CallKeyboardFilter(const OrbisImeKeycode* src_keycode, u16* out_keycode, u32* out_status); + + bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text, + std::size_t native_text_len); + bool ConvertUTF8ToOrbis(const char* native_text, std::size_t utf8_text_len, + char16_t* orbis_text, std::size_t orbis_text_len); +}; + +class ImeDialogUi final : public ImGui::Layer { + ImeDialogState* state{}; + OrbisImeDialogStatus* status{}; + OrbisImeDialogResult* result{}; + + bool first_render = true; + std::mutex draw_mutex; + +public: + explicit ImeDialogUi(ImeDialogState* state = nullptr, OrbisImeDialogStatus* status = nullptr, + OrbisImeDialogResult* result = nullptr); + ~ImeDialogUi() override; + ImeDialogUi(const ImeDialogUi& other) = delete; + ImeDialogUi(ImeDialogUi&& other) noexcept; + ImeDialogUi& operator=(ImeDialogUi&& other); + + void Draw() override; + +private: + void Free(); + + void DrawInputText(); + void DrawMultiLineInputText(); + + static int InputTextCallback(ImGuiInputTextCallbackData* data); +}; + +} // namespace Libraries::ImeDialog diff --git a/src/core/libraries/error_codes.h b/src/core/libraries/error_codes.h index b9896b6c3..041870ed7 100644 --- a/src/core/libraries/error_codes.h +++ b/src/core/libraries/error_codes.h @@ -498,4 +498,12 @@ constexpr int ORBIS_AVPLAYER_ERROR_INFO_OTHER_ENCRY = 0x806A00BF; // AppContent library constexpr int ORBIS_APP_CONTENT_ERROR_PARAMETER = 0x80D90002; constexpr int ORBIS_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT = 0x80D90007; -constexpr int ORBIS_APP_CONTENT_ERROR_NOT_FOUND = 0x80D90005; \ No newline at end of file +constexpr int ORBIS_APP_CONTENT_ERROR_NOT_FOUND = 0x80D90005; + +// Fiber library +constexpr int ORBIS_FIBER_ERROR_NULL = 0x80590001; +constexpr int ORBIS_FIBER_ERROR_ALIGNMENT = 0x80590002; +constexpr int ORBIS_FIBER_ERROR_RANGE = 0x80590003; +constexpr int ORBIS_FIBER_ERROR_INVALID = 0x80590004; +constexpr int ORBIS_FIBER_ERROR_PERMISSION = 0x80590005; +constexpr int ORBIS_FIBER_ERROR_STATE = 0x80590006; \ No newline at end of file diff --git a/src/core/libraries/fiber/fiber.cpp b/src/core/libraries/fiber/fiber.cpp new file mode 100644 index 000000000..bd1575dda --- /dev/null +++ b/src/core/libraries/fiber/fiber.cpp @@ -0,0 +1,284 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "fiber.h" + +#include "common/logging/log.h" +#include "common/singleton.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/linker.h" + +#ifdef _WIN64 +#include +#endif + +namespace Libraries::Fiber { + +constexpr static u64 kFiberSignature = 0x054ad954; + +thread_local SceFiber* gCurrentFiber = nullptr; +thread_local void* gFiberThread = nullptr; + +void FiberEntry(void* param) { + SceFiber* fiber = static_cast(param); + u64 argRun = 0; + u64 argRet = 0; + + gCurrentFiber = fiber; + + if (fiber->pArgRun != nullptr) { + argRun = *fiber->pArgRun; + } + + const auto* linker = Common::Singleton::Instance(); + linker->ExecuteGuest(fiber->entry, fiber->argOnInitialize, argRun); + + UNREACHABLE(); +} + +s32 PS4_SYSV_ABI sceFiberInitialize(SceFiber* fiber, const char* name, SceFiberEntry entry, + u64 argOnInitialize, void* addrContext, u64 sizeContext, + const SceFiberOptParam* optParam) { + LOG_INFO(Lib_Fiber, "called: name = {}", name); + + if (!fiber || !name || !entry) { + return ORBIS_FIBER_ERROR_NULL; + } + + fiber->signature = kFiberSignature; + + fiber->entry = entry; + fiber->argOnInitialize = argOnInitialize; + + fiber->argRun = 0; + fiber->pArgRun = &fiber->argRun; + fiber->argReturn = 0; + fiber->pArgReturn = &fiber->argReturn; + + fiber->sizeContext = sizeContext; + + fiber->state = FiberState::Init; +#ifdef _WIN64 + fiber->handle = CreateFiber(sizeContext, FiberEntry, fiber); +#else + UNREACHABLE_MSG("Missing implementation"); +#endif + strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH); + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberOptParamInitialize(SceFiberOptParam* optParam) { + LOG_ERROR(Lib_Fiber, "called"); + + if (!optParam) { + return ORBIS_FIBER_ERROR_NULL; + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberFinalize(SceFiber* fiber) { + LOG_TRACE(Lib_Fiber, "called"); + + if (!fiber) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber % 8 != 0) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (fiber->signature != kFiberSignature) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (fiber->state != FiberState::Run) { + return ORBIS_FIBER_ERROR_STATE; + } + + fiber->signature = 0; + fiber->state = FiberState::None; + +#ifdef _WIN64 + DeleteFiber(fiber->handle); +#else + UNREACHABLE_MSG("Missing implementation"); +#endif + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberRun(SceFiber* fiber, u64 argOnRunTo, u64* argOnReturn) { + LOG_TRACE(Lib_Fiber, "called"); + + if (!fiber) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber % 8 != 0) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (fiber->signature != kFiberSignature) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (fiber->state == FiberState::Run) { + return ORBIS_FIBER_ERROR_STATE; + } + + if (gFiberThread == nullptr) { +#ifdef _WIN64 + gFiberThread = ConvertThreadToFiber(nullptr); +#else + UNREACHABLE_MSG("Missing implementation"); +#endif + } + + gCurrentFiber = fiber; + + if (fiber->pArgRun != nullptr) { + *fiber->pArgRun = argOnRunTo; + } + + fiber->pArgReturn = argOnReturn; + fiber->state = FiberState::Run; +#ifdef _WIN64 + SwitchToFiber(fiber->handle); +#else + UNREACHABLE_MSG("Missing implementation"); +#endif + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberSwitch(SceFiber* fiber, u64 argOnRunTo, u64* argOnRun) { + LOG_TRACE(Lib_Fiber, "called"); + + if (!fiber) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber % 8 != 0) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + if (fiber->signature != kFiberSignature) { + return ORBIS_FIBER_ERROR_INVALID; + } + if (gCurrentFiber == nullptr) { + return ORBIS_FIBER_ERROR_PERMISSION; + } + if (fiber->state == FiberState::Run) { + return ORBIS_FIBER_ERROR_STATE; + } + + gCurrentFiber->state = FiberState::Suspend; + + // TODO: argOnRun + + *fiber->pArgRun = argOnRunTo; + fiber->state = FiberState::Run; + + gCurrentFiber = fiber; +#ifdef _WIN64 + SwitchToFiber(fiber->handle); +#else + UNREACHABLE_MSG("Missing implementation"); +#endif + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberGetSelf(SceFiber** fiber) { + LOG_TRACE(Lib_Fiber, "called"); + + if (!fiber || !gCurrentFiber) { + return ORBIS_FIBER_ERROR_NULL; + } + if (gCurrentFiber->signature != kFiberSignature) { + return ORBIS_FIBER_ERROR_PERMISSION; + } + + *fiber = gCurrentFiber; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 argOnReturn, u64* argOnRun) { + LOG_TRACE(Lib_Fiber, "called"); + + if (gCurrentFiber->signature != kFiberSignature) { + return ORBIS_FIBER_ERROR_PERMISSION; + } + + if (gCurrentFiber->pArgReturn != nullptr) { + *gCurrentFiber->pArgReturn = argOnReturn; + } + + // TODO: argOnRun + gCurrentFiber->state = FiberState::Suspend; + gCurrentFiber = nullptr; +#ifdef _WIN64 + SwitchToFiber(gFiberThread); +#else + UNREACHABLE_MSG("Missing implementation"); +#endif + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberGetInfo(SceFiber* fiber, SceFiberInfo* fiberInfo) { + LOG_INFO(Lib_Fiber, "called"); + + if (!fiber || !fiberInfo) { + return ORBIS_FIBER_ERROR_NULL; + } + + fiberInfo->entry = fiber->entry; + fiberInfo->argOnInitialize = fiber->argOnInitialize; + fiberInfo->addrContext = nullptr; + fiberInfo->sizeContext = fiber->sizeContext; + fiberInfo->sizeContextMargin = 0; + + strncpy(fiberInfo->name, fiber->name, ORBIS_FIBER_MAX_NAME_LENGTH); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags) { + LOG_ERROR(Lib_Fiber, "called"); + + if (flags != 0) { + return ORBIS_FIBER_ERROR_INVALID; + } + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck() { + LOG_ERROR(Lib_Fiber, "called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceFiberRename(SceFiber* fiber, const char* name) { + LOG_INFO(Lib_Fiber, "called, name = {}", name); + + if (!fiber || !name) { + return ORBIS_FIBER_ERROR_NULL; + } + if ((u64)fiber % 8 != 0) { + return ORBIS_FIBER_ERROR_ALIGNMENT; + } + + strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH); + return ORBIS_OK; +} + +void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize); + LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize); + LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize); + + LIB_FUNCTION("a0LLrZWac0M", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRun); + LIB_FUNCTION("PFT2S-tJ7Uk", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberSwitch); + LIB_FUNCTION("p+zLIOg27zU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetSelf); + LIB_FUNCTION("B0ZX2hx9DMw", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberReturnToThread); + + LIB_FUNCTION("uq2Y5BFz0PE", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetInfo); + LIB_FUNCTION("Lcqty+QNWFc", "libSceFiber", 1, "libSceFiber", 1, 1, + sceFiberStartContextSizeCheck); + LIB_FUNCTION("Kj4nXMpnM8Y", "libSceFiber", 1, "libSceFiber", 1, 1, + sceFiberStopContextSizeCheck); + LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename); +} + +} // namespace Libraries::Fiber \ No newline at end of file diff --git a/src/core/libraries/fiber/fiber.h b/src/core/libraries/fiber/fiber.h new file mode 100644 index 000000000..930409caa --- /dev/null +++ b/src/core/libraries/fiber/fiber.h @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/assert.h" +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} +namespace Libraries::Fiber { + +#define ORBIS_FIBER_MAX_NAME_LENGTH (31) + +typedef void PS4_SYSV_ABI (*SceFiberEntry)(u64 argOnInitialize, u64 argOnRun); + +enum FiberState : u32 { + None = 0u, + Init = 1u, + Run = 2u, + Suspend = 3u, +}; + +struct SceFiber { + u64 signature; + FiberState state; + SceFiberEntry entry; + + u64 argOnInitialize; + + u64 argRun; + u64* pArgRun; + + u64 argReturn; + u64* pArgReturn; + + u64 sizeContext; + + char name[ORBIS_FIBER_MAX_NAME_LENGTH]; + void* handle; +}; +static_assert(sizeof(SceFiber) <= 256); + +struct SceFiberInfo { + u64 size; + SceFiberEntry entry; + u64 argOnInitialize; + void* addrContext; + u64 sizeContext; + char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1]; + u64 sizeContextMargin; +}; +static_assert(sizeof(SceFiberInfo) <= 128); + +typedef void* SceFiberOptParam; + +s32 PS4_SYSV_ABI sceFiberInitialize(SceFiber* fiber, const char* name, SceFiberEntry entry, + u64 argOnInitialize, void* addrContext, u64 sizeContext, + const SceFiberOptParam* optParam); + +s32 PS4_SYSV_ABI sceFiberOptParamInitialize(SceFiberOptParam* optParam); + +s32 PS4_SYSV_ABI sceFiberFinalize(SceFiber* fiber); + +s32 PS4_SYSV_ABI sceFiberRun(SceFiber* fiber, u64 argOnRunTo, u64* argOnReturn); + +s32 PS4_SYSV_ABI sceFiberSwitch(SceFiber* fiber, u64 argOnRunTo, u64* argOnRun); + +s32 PS4_SYSV_ABI sceFiberGetSelf(SceFiber** fiber); + +s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 argOnReturn, u64* argOnRun); + +s32 PS4_SYSV_ABI sceFiberGetInfo(SceFiber* fiber, SceFiberInfo* fiberInfo); + +s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags); + +s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck(void); + +s32 PS4_SYSV_ABI sceFiberRename(SceFiber* fiber, const char* name); + +void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Fiber \ No newline at end of file diff --git a/src/core/libraries/game_live_streaming/gamelivestreaming.cpp b/src/core/libraries/game_live_streaming/gamelivestreaming.cpp new file mode 100644 index 000000000..a1ebddfbf --- /dev/null +++ b/src/core/libraries/game_live_streaming/gamelivestreaming.cpp @@ -0,0 +1,354 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "gamelivestreaming.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" + +namespace Libraries::GameLiveStreaming { + +int PS4_SYSV_ABI sceGameLiveStreamingStartDebugBroadcast() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingStopDebugBroadcast() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingApplySocialFeedbackMessageFilter() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingCheckCallback() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingClearPresetSocialFeedbackCommands() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingClearSocialFeedbackMessages() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingClearSpoilerTag() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingEnableLiveStreaming() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingEnableSocialFeedback() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentBroadcastScreenLayout() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentStatus(OrbisGameLiveStreamingStatus* status) { + memset(status, 0, sizeof(*status)); + status->isOnAir = false; + LOG_DEBUG(Lib_GameLiveStreaming, "(STUBBED) called userid = {}", status->userId); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentStatus2() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingGetProgramInfo() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingGetSocialFeedbackMessages() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingGetSocialFeedbackMessagesCount() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingInitialize() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingLaunchLiveViewer() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingLaunchLiveViewerA() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingPermitLiveStreaming() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingPermitServerSideRecording() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingPostSocialMessage() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingRegisterCallback() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingScreenCloseSeparateMode() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingScreenConfigureSeparateMode() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingScreenInitialize() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingScreenInitializeSeparateModeParameter() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingScreenOpenSeparateMode() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingScreenSetMode() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingScreenTerminate() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetCameraFrameSetting() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetDefaultServiceProviderPermission() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetGuardAreas() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetInvitationSessionId() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetLinkCommentPreset() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetMaxBitrate() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetMetadata() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetPresetSocialFeedbackCommands() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetPresetSocialFeedbackCommandsDescription() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetServiceProviderPermission() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetSpoilerTag() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingSetStandbyScreenResource() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingStartGenerateStandbyScreenResource() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingStartSocialFeedbackMessageFiltering() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingStopGenerateStandbyScreenResource() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingStopSocialFeedbackMessageFiltering() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingTerminate() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceGameLiveStreamingUnregisterCallback() { + LOG_ERROR(Lib_GameLiveStreaming, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceGameLiveStreaming(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("caqgDl+V9qA", "libSceGameLiveStreaming_debug", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingStartDebugBroadcast); + LIB_FUNCTION("0i8Lrllxwow", "libSceGameLiveStreaming_debug", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingStopDebugBroadcast); + LIB_FUNCTION("NqkTzemliC0", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingApplySocialFeedbackMessageFilter); + LIB_FUNCTION("PC4jq87+YQI", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingCheckCallback); + LIB_FUNCTION("FcHBfHjFXkA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingClearPresetSocialFeedbackCommands); + LIB_FUNCTION("lZ2Sd0uEvpo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingClearSocialFeedbackMessages); + LIB_FUNCTION("6c2zGtThFww", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingClearSpoilerTag); + LIB_FUNCTION("dWM80AX39o4", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingEnableLiveStreaming); + LIB_FUNCTION("wBOQWjbWMfU", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingEnableSocialFeedback); + LIB_FUNCTION("aRSQNqbats4", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingGetCurrentBroadcastScreenLayout); + LIB_FUNCTION("CoPMx369EqM", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingGetCurrentStatus); + LIB_FUNCTION("lK8dLBNp9OE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingGetCurrentStatus2); + LIB_FUNCTION("OIIm19xu+NM", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingGetProgramInfo); + LIB_FUNCTION("PMx7N4WqNdo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingGetSocialFeedbackMessages); + LIB_FUNCTION("yeQKjHETi40", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingGetSocialFeedbackMessagesCount); + LIB_FUNCTION("kvYEw2lBndk", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingInitialize); + LIB_FUNCTION("ysWfX5PPbfc", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingLaunchLiveViewer); + LIB_FUNCTION("cvRCb7DTAig", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingLaunchLiveViewerA); + LIB_FUNCTION("K0QxEbD7q+c", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingPermitLiveStreaming); + LIB_FUNCTION("-EHnU68gExU", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingPermitServerSideRecording); + LIB_FUNCTION("hggKhPySVgI", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingPostSocialMessage); + LIB_FUNCTION("nFP8qT9YXbo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingRegisterCallback); + LIB_FUNCTION("b5RaMD2J0So", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingScreenCloseSeparateMode); + LIB_FUNCTION("hBdd8n6kuvE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingScreenConfigureSeparateMode); + LIB_FUNCTION("uhCmn81s-mU", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingScreenInitialize); + LIB_FUNCTION("fo5B8RUaBxQ", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingScreenInitializeSeparateModeParameter); + LIB_FUNCTION("iorzW0pKOiA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingScreenOpenSeparateMode); + LIB_FUNCTION("gDSvt78H3Oo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingScreenSetMode); + LIB_FUNCTION("HE93dr-5rx4", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingScreenTerminate); + LIB_FUNCTION("3PSiwAzFISE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetCameraFrameSetting); + LIB_FUNCTION("TwuUzTKKeek", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetDefaultServiceProviderPermission); + LIB_FUNCTION("Gw6S4oqlY7E", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetGuardAreas); + LIB_FUNCTION("QmQYwQ7OTJI", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetInvitationSessionId); + LIB_FUNCTION("Sb5bAXyUt5c", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetLinkCommentPreset); + LIB_FUNCTION("q-kxuaF7URU", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetMaxBitrate); + LIB_FUNCTION("hUY-mSOyGL0", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetMetadata); + LIB_FUNCTION("ycodiP2I0xo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetPresetSocialFeedbackCommands); + LIB_FUNCTION("x6deXUpQbBo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetPresetSocialFeedbackCommandsDescription); + LIB_FUNCTION("mCoz3k3zPmA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetServiceProviderPermission); + LIB_FUNCTION("ZuX+zzz2DkA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetSpoilerTag); + LIB_FUNCTION("MLvYI86FFAo", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingSetStandbyScreenResource); + LIB_FUNCTION("y0KkAydy9xE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingStartGenerateStandbyScreenResource); + LIB_FUNCTION("Y1WxX7dPMCw", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingStartSocialFeedbackMessageFiltering); + LIB_FUNCTION("D7dg5QJ4FlE", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingStopGenerateStandbyScreenResource); + LIB_FUNCTION("bYuGUBuIsaY", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingStopSocialFeedbackMessageFiltering); + LIB_FUNCTION("9yK6Fk8mKOQ", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingTerminate); + LIB_FUNCTION("5XHaH3kL+bA", "libSceGameLiveStreaming", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingUnregisterCallback); + LIB_FUNCTION("caqgDl+V9qA", "libSceGameLiveStreaming_direct_streaming", 1, + "libSceGameLiveStreaming", 1, 1, sceGameLiveStreamingStartDebugBroadcast); + LIB_FUNCTION("0i8Lrllxwow", "libSceGameLiveStreaming_direct_streaming", 1, + "libSceGameLiveStreaming", 1, 1, sceGameLiveStreamingStopDebugBroadcast); + LIB_FUNCTION("CoPMx369EqM", "libSceGameLiveStreamingCompat", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingGetCurrentStatus); + LIB_FUNCTION("ysWfX5PPbfc", "libSceGameLiveStreamingCompat", 1, "libSceGameLiveStreaming", 1, 1, + sceGameLiveStreamingLaunchLiveViewer); +}; + +} // namespace Libraries::GameLiveStreaming \ No newline at end of file diff --git a/src/core/libraries/game_live_streaming/gamelivestreaming.h b/src/core/libraries/game_live_streaming/gamelivestreaming.h new file mode 100644 index 000000000..468750fd1 --- /dev/null +++ b/src/core/libraries/game_live_streaming/gamelivestreaming.h @@ -0,0 +1,81 @@ +// 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::GameLiveStreaming { + +struct OrbisGameLiveStreamingStatus { + bool isOnAir; + u8 align[3]; + u32 spectatorCounts; + s32 userId; + u8 reserved[60]; +}; +struct OrbisGameLiveStreamingStatus2 { + s32 userId; + bool isOnAir; + u8 align[3]; + u32 spectatorCounts; + u32 textMessageCounts; + u32 commandMessageCounts; + u32 broadcastVideoResolution; + u8 reserved[48]; +}; + +int PS4_SYSV_ABI sceGameLiveStreamingStartDebugBroadcast(); +int PS4_SYSV_ABI sceGameLiveStreamingStopDebugBroadcast(); +int PS4_SYSV_ABI sceGameLiveStreamingApplySocialFeedbackMessageFilter(); +int PS4_SYSV_ABI sceGameLiveStreamingCheckCallback(); +int PS4_SYSV_ABI sceGameLiveStreamingClearPresetSocialFeedbackCommands(); +int PS4_SYSV_ABI sceGameLiveStreamingClearSocialFeedbackMessages(); +int PS4_SYSV_ABI sceGameLiveStreamingClearSpoilerTag(); +int PS4_SYSV_ABI sceGameLiveStreamingEnableLiveStreaming(); +int PS4_SYSV_ABI sceGameLiveStreamingEnableSocialFeedback(); +int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentBroadcastScreenLayout(); +int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentStatus(OrbisGameLiveStreamingStatus* status); +int PS4_SYSV_ABI sceGameLiveStreamingGetCurrentStatus2(); +int PS4_SYSV_ABI sceGameLiveStreamingGetProgramInfo(); +int PS4_SYSV_ABI sceGameLiveStreamingGetSocialFeedbackMessages(); +int PS4_SYSV_ABI sceGameLiveStreamingGetSocialFeedbackMessagesCount(); +int PS4_SYSV_ABI sceGameLiveStreamingInitialize(); +int PS4_SYSV_ABI sceGameLiveStreamingLaunchLiveViewer(); +int PS4_SYSV_ABI sceGameLiveStreamingLaunchLiveViewerA(); +int PS4_SYSV_ABI sceGameLiveStreamingPermitLiveStreaming(); +int PS4_SYSV_ABI sceGameLiveStreamingPermitServerSideRecording(); +int PS4_SYSV_ABI sceGameLiveStreamingPostSocialMessage(); +int PS4_SYSV_ABI sceGameLiveStreamingRegisterCallback(); +int PS4_SYSV_ABI sceGameLiveStreamingScreenCloseSeparateMode(); +int PS4_SYSV_ABI sceGameLiveStreamingScreenConfigureSeparateMode(); +int PS4_SYSV_ABI sceGameLiveStreamingScreenInitialize(); +int PS4_SYSV_ABI sceGameLiveStreamingScreenInitializeSeparateModeParameter(); +int PS4_SYSV_ABI sceGameLiveStreamingScreenOpenSeparateMode(); +int PS4_SYSV_ABI sceGameLiveStreamingScreenSetMode(); +int PS4_SYSV_ABI sceGameLiveStreamingScreenTerminate(); +int PS4_SYSV_ABI sceGameLiveStreamingSetCameraFrameSetting(); +int PS4_SYSV_ABI sceGameLiveStreamingSetDefaultServiceProviderPermission(); +int PS4_SYSV_ABI sceGameLiveStreamingSetGuardAreas(); +int PS4_SYSV_ABI sceGameLiveStreamingSetInvitationSessionId(); +int PS4_SYSV_ABI sceGameLiveStreamingSetLinkCommentPreset(); +int PS4_SYSV_ABI sceGameLiveStreamingSetMaxBitrate(); +int PS4_SYSV_ABI sceGameLiveStreamingSetMetadata(); +int PS4_SYSV_ABI sceGameLiveStreamingSetPresetSocialFeedbackCommands(); +int PS4_SYSV_ABI sceGameLiveStreamingSetPresetSocialFeedbackCommandsDescription(); +int PS4_SYSV_ABI sceGameLiveStreamingSetServiceProviderPermission(); +int PS4_SYSV_ABI sceGameLiveStreamingSetSpoilerTag(); +int PS4_SYSV_ABI sceGameLiveStreamingSetStandbyScreenResource(); +int PS4_SYSV_ABI sceGameLiveStreamingStartGenerateStandbyScreenResource(); +int PS4_SYSV_ABI sceGameLiveStreamingStartSocialFeedbackMessageFiltering(); +int PS4_SYSV_ABI sceGameLiveStreamingStopGenerateStandbyScreenResource(); +int PS4_SYSV_ABI sceGameLiveStreamingStopSocialFeedbackMessageFiltering(); +int PS4_SYSV_ABI sceGameLiveStreamingTerminate(); +int PS4_SYSV_ABI sceGameLiveStreamingUnregisterCallback(); + +void RegisterlibSceGameLiveStreaming(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::GameLiveStreaming \ No newline at end of file diff --git a/src/core/libraries/gnmdriver/gnmdriver.cpp b/src/core/libraries/gnmdriver/gnmdriver.cpp index 7e2153efa..ce30895ca 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.cpp +++ b/src/core/libraries/gnmdriver/gnmdriver.cpp @@ -1076,9 +1076,27 @@ s32 PS4_SYSV_ABI sceGnmInsertPopMarker(u32* cmdbuf, u32 size) { return -1; } -int PS4_SYSV_ABI sceGnmInsertPushColorMarker() { - LOG_ERROR(Lib_GnmDriver, "(STUBBED) called"); - return ORBIS_OK; +s32 PS4_SYSV_ABI sceGnmInsertPushColorMarker(u32* cmdbuf, u32 size, const char* marker, u32 color) { + LOG_TRACE(Lib_GnmDriver, "called"); + + if (cmdbuf && marker) { + const auto len = std::strlen(marker); + const u32 packet_size = ((len + 0xc) >> 2) + ((len + 0x10) >> 3) * 2; + if (packet_size + 2 == size) { + auto* nop = reinterpret_cast(cmdbuf); + nop->header = + PM4Type3Header{PM4ItOpcode::Nop, packet_size, PM4ShaderType::ShaderGraphics}; + nop->data_block[0] = PM4CmdNop::PayloadType::DebugColorMarkerPush; + const auto marker_len = len + 1; + std::memcpy(&nop->data_block[1], marker, marker_len); + *reinterpret_cast(reinterpret_cast(&nop->data_block[1]) + marker_len + 8) = + color; + std::memset(reinterpret_cast(&nop->data_block[1]) + marker_len + 8 + sizeof(u32), + 0, packet_size * 4 - marker_len - 8 - sizeof(u32)); + return ORBIS_OK; + } + } + return -1; } s32 PS4_SYSV_ABI sceGnmInsertPushMarker(u32* cmdbuf, u32 size, const char* marker) { diff --git a/src/core/libraries/gnmdriver/gnmdriver.h b/src/core/libraries/gnmdriver/gnmdriver.h index 55a70cbf3..33bccf427 100644 --- a/src/core/libraries/gnmdriver/gnmdriver.h +++ b/src/core/libraries/gnmdriver/gnmdriver.h @@ -105,7 +105,7 @@ int PS4_SYSV_ABI sceGnmGpuPaDebugEnter(); int PS4_SYSV_ABI sceGnmGpuPaDebugLeave(); int PS4_SYSV_ABI sceGnmInsertDingDongMarker(); s32 PS4_SYSV_ABI sceGnmInsertPopMarker(u32* cmdbuf, u32 size); -int PS4_SYSV_ABI sceGnmInsertPushColorMarker(); +s32 PS4_SYSV_ABI sceGnmInsertPushColorMarker(u32* cmdbuf, u32 size, const char* marker, u32 color); s32 PS4_SYSV_ABI sceGnmInsertPushMarker(u32* cmdbuf, u32 size, const char* marker); int PS4_SYSV_ABI sceGnmInsertSetColorMarker(); int PS4_SYSV_ABI sceGnmInsertSetMarker(); diff --git a/src/core/libraries/ime/ime.cpp b/src/core/libraries/ime/ime.cpp new file mode 100644 index 000000000..13a70acf7 --- /dev/null +++ b/src/core/libraries/ime/ime.cpp @@ -0,0 +1,340 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "ime.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" + +namespace Libraries::Ime { + +int PS4_SYSV_ABI FinalizeImeModule() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI InitializeImeModule() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeCheckFilterText() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeCheckRemoteEventParam() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeCheckUpdateTextInfo() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeClose() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeConfigGet() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeConfigSet() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeConfirmCandidate() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeDicAddWord() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeDicDeleteLearnDics() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeDicDeleteUserDics() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeDicDeleteWord() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeDicGetWords() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeDicReplaceWord() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeDisableController() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeFilterText() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeForTestFunction() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeGetPanelPositionAndForm() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeGetPanelSize() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeKeyboardClose() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeKeyboardGetInfo() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeKeyboardGetResourceId() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeKeyboardOpen() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeKeyboardOpenInternal() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeKeyboardSetMode() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeKeyboardUpdate() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeOpen() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeOpenInternal() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeParamInit() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeSetCandidateIndex() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeSetCaret() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeSetText() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeSetTextGeometry() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeUpdate() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshClearPreedit() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshClose() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshConfirmPreedit() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshDisableController() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshGetPanelPositionAndForm() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshInformConfirmdString() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshInformConfirmdString2() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshOpen() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSendTextInfo() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSetCaretGeometry() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSetCaretIndexInPreedit() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSetPanelPosition() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSetParam() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSetPreeditGeometry() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSetSelectGeometry() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshSetSelectionText() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshUpdate() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshUpdateContext() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceImeVshUpdateContext2() { + LOG_ERROR(Lib_Ime, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceIme(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("mN+ZoSN-8hQ", "libSceIme", 1, "libSceIme", 1, 1, FinalizeImeModule); + LIB_FUNCTION("uTW+63goeJs", "libSceIme", 1, "libSceIme", 1, 1, InitializeImeModule); + LIB_FUNCTION("Lf3DeGWC6xg", "libSceIme", 1, "libSceIme", 1, 1, sceImeCheckFilterText); + LIB_FUNCTION("zHuMUGb-AQI", "libSceIme", 1, "libSceIme", 1, 1, sceImeCheckRemoteEventParam); + LIB_FUNCTION("OTb0Mg+1i1k", "libSceIme", 1, "libSceIme", 1, 1, sceImeCheckUpdateTextInfo); + LIB_FUNCTION("TmVP8LzcFcY", "libSceIme", 1, "libSceIme", 1, 1, sceImeClose); + LIB_FUNCTION("Ho5NVQzpKHo", "libSceIme", 1, "libSceIme", 1, 1, sceImeConfigGet); + LIB_FUNCTION("P5dPeiLwm-M", "libSceIme", 1, "libSceIme", 1, 1, sceImeConfigSet); + LIB_FUNCTION("tKLmVIUkpyM", "libSceIme", 1, "libSceIme", 1, 1, sceImeConfirmCandidate); + LIB_FUNCTION("NYDsL9a0oEo", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicAddWord); + LIB_FUNCTION("l01GKoyiQrY", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicDeleteLearnDics); + LIB_FUNCTION("E2OcGgi-FPY", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicDeleteUserDics); + LIB_FUNCTION("JAiMBkOTYKI", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicDeleteWord); + LIB_FUNCTION("JoPdCUXOzMU", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicGetWords); + LIB_FUNCTION("FuEl46uHDyo", "libSceIme", 1, "libSceIme", 1, 1, sceImeDicReplaceWord); + LIB_FUNCTION("E+f1n8e8DAw", "libSceIme", 1, "libSceIme", 1, 1, sceImeDisableController); + LIB_FUNCTION("evjOsE18yuI", "libSceIme", 1, "libSceIme", 1, 1, sceImeFilterText); + LIB_FUNCTION("wVkehxutK-U", "libSceIme", 1, "libSceIme", 1, 1, sceImeForTestFunction); + LIB_FUNCTION("T6FYjZXG93o", "libSceIme", 1, "libSceIme", 1, 1, sceImeGetPanelPositionAndForm); + LIB_FUNCTION("ziPDcIjO0Vk", "libSceIme", 1, "libSceIme", 1, 1, sceImeGetPanelSize); + LIB_FUNCTION("PMVehSlfZ94", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardClose); + LIB_FUNCTION("VkqLPArfFdc", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardGetInfo); + LIB_FUNCTION("dKadqZFgKKQ", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardGetResourceId); + LIB_FUNCTION("eaFXjfJv3xs", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardOpen); + LIB_FUNCTION("oYkJlMK51SA", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardOpenInternal); + LIB_FUNCTION("ua+13Hk9kKs", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardSetMode); + LIB_FUNCTION("3Hx2Uw9xnv8", "libSceIme", 1, "libSceIme", 1, 1, sceImeKeyboardUpdate); + LIB_FUNCTION("RPydv-Jr1bc", "libSceIme", 1, "libSceIme", 1, 1, sceImeOpen); + LIB_FUNCTION("16UI54cWRQk", "libSceIme", 1, "libSceIme", 1, 1, sceImeOpenInternal); + LIB_FUNCTION("WmYDzdC4EHI", "libSceIme", 1, "libSceIme", 1, 1, sceImeParamInit); + LIB_FUNCTION("TQaogSaqkEk", "libSceIme", 1, "libSceIme", 1, 1, sceImeSetCandidateIndex); + LIB_FUNCTION("WLxUN2WMim8", "libSceIme", 1, "libSceIme", 1, 1, sceImeSetCaret); + LIB_FUNCTION("ieCNrVrzKd4", "libSceIme", 1, "libSceIme", 1, 1, sceImeSetText); + LIB_FUNCTION("TXYHFRuL8UY", "libSceIme", 1, "libSceIme", 1, 1, sceImeSetTextGeometry); + LIB_FUNCTION("-4GCfYdNF1s", "libSceIme", 1, "libSceIme", 1, 1, sceImeUpdate); + LIB_FUNCTION("oOwl47ouxoM", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshClearPreedit); + LIB_FUNCTION("gtoTsGM9vEY", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshClose); + LIB_FUNCTION("wTKF4mUlSew", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshConfirmPreedit); + LIB_FUNCTION("rM-1hkuOhh0", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshDisableController); + LIB_FUNCTION("42xMaQ+GLeQ", "libSceIme", 1, "libSceIme", 1, 1, + sceImeVshGetPanelPositionAndForm); + LIB_FUNCTION("ZmmV6iukhyo", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshInformConfirmdString); + LIB_FUNCTION("EQBusz6Uhp8", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshInformConfirmdString2); + LIB_FUNCTION("LBicRa-hj3A", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshOpen); + LIB_FUNCTION("-IAOwd2nO7g", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSendTextInfo); + LIB_FUNCTION("qDagOjvJdNk", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetCaretGeometry); + LIB_FUNCTION("tNOlmxee-Nk", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetCaretIndexInPreedit); + LIB_FUNCTION("rASXozKkQ9g", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetPanelPosition); + LIB_FUNCTION("idvMaIu5H+k", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetParam); + LIB_FUNCTION("ga5GOgThbjo", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetPreeditGeometry); + LIB_FUNCTION("RuSca8rS6yA", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetSelectGeometry); + LIB_FUNCTION("J7COZrgSFRA", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshSetSelectionText); + LIB_FUNCTION("WqAayyok5p0", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshUpdate); + LIB_FUNCTION("O7Fdd+Oc-qQ", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshUpdateContext); + LIB_FUNCTION("fwcPR7+7Rks", "libSceIme", 1, "libSceIme", 1, 1, sceImeVshUpdateContext2); +}; + +} // namespace Libraries::Ime \ No newline at end of file diff --git a/src/core/libraries/ime/ime.h b/src/core/libraries/ime/ime.h new file mode 100644 index 000000000..807616f14 --- /dev/null +++ b/src/core/libraries/ime/ime.h @@ -0,0 +1,70 @@ +// 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::Ime { + +int PS4_SYSV_ABI FinalizeImeModule(); +int PS4_SYSV_ABI InitializeImeModule(); +int PS4_SYSV_ABI sceImeCheckFilterText(); +int PS4_SYSV_ABI sceImeCheckRemoteEventParam(); +int PS4_SYSV_ABI sceImeCheckUpdateTextInfo(); +int PS4_SYSV_ABI sceImeClose(); +int PS4_SYSV_ABI sceImeConfigGet(); +int PS4_SYSV_ABI sceImeConfigSet(); +int PS4_SYSV_ABI sceImeConfirmCandidate(); +int PS4_SYSV_ABI sceImeDicAddWord(); +int PS4_SYSV_ABI sceImeDicDeleteLearnDics(); +int PS4_SYSV_ABI sceImeDicDeleteUserDics(); +int PS4_SYSV_ABI sceImeDicDeleteWord(); +int PS4_SYSV_ABI sceImeDicGetWords(); +int PS4_SYSV_ABI sceImeDicReplaceWord(); +int PS4_SYSV_ABI sceImeDisableController(); +int PS4_SYSV_ABI sceImeFilterText(); +int PS4_SYSV_ABI sceImeForTestFunction(); +int PS4_SYSV_ABI sceImeGetPanelPositionAndForm(); +int PS4_SYSV_ABI sceImeGetPanelSize(); +int PS4_SYSV_ABI sceImeKeyboardClose(); +int PS4_SYSV_ABI sceImeKeyboardGetInfo(); +int PS4_SYSV_ABI sceImeKeyboardGetResourceId(); +int PS4_SYSV_ABI sceImeKeyboardOpen(); +int PS4_SYSV_ABI sceImeKeyboardOpenInternal(); +int PS4_SYSV_ABI sceImeKeyboardSetMode(); +int PS4_SYSV_ABI sceImeKeyboardUpdate(); +int PS4_SYSV_ABI sceImeOpen(); +int PS4_SYSV_ABI sceImeOpenInternal(); +int PS4_SYSV_ABI sceImeParamInit(); +int PS4_SYSV_ABI sceImeSetCandidateIndex(); +int PS4_SYSV_ABI sceImeSetCaret(); +int PS4_SYSV_ABI sceImeSetText(); +int PS4_SYSV_ABI sceImeSetTextGeometry(); +int PS4_SYSV_ABI sceImeUpdate(); +int PS4_SYSV_ABI sceImeVshClearPreedit(); +int PS4_SYSV_ABI sceImeVshClose(); +int PS4_SYSV_ABI sceImeVshConfirmPreedit(); +int PS4_SYSV_ABI sceImeVshDisableController(); +int PS4_SYSV_ABI sceImeVshGetPanelPositionAndForm(); +int PS4_SYSV_ABI sceImeVshInformConfirmdString(); +int PS4_SYSV_ABI sceImeVshInformConfirmdString2(); +int PS4_SYSV_ABI sceImeVshOpen(); +int PS4_SYSV_ABI sceImeVshSendTextInfo(); +int PS4_SYSV_ABI sceImeVshSetCaretGeometry(); +int PS4_SYSV_ABI sceImeVshSetCaretIndexInPreedit(); +int PS4_SYSV_ABI sceImeVshSetPanelPosition(); +int PS4_SYSV_ABI sceImeVshSetParam(); +int PS4_SYSV_ABI sceImeVshSetPreeditGeometry(); +int PS4_SYSV_ABI sceImeVshSetSelectGeometry(); +int PS4_SYSV_ABI sceImeVshSetSelectionText(); +int PS4_SYSV_ABI sceImeVshUpdate(); +int PS4_SYSV_ABI sceImeVshUpdateContext(); +int PS4_SYSV_ABI sceImeVshUpdateContext2(); + +void RegisterlibSceIme(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Ime \ No newline at end of file diff --git a/src/core/libraries/kernel/time_management.cpp b/src/core/libraries/kernel/time_management.cpp index 5fa26b789..853f8d54c 100644 --- a/src/core/libraries/kernel/time_management.cpp +++ b/src/core/libraries/kernel/time_management.cpp @@ -148,7 +148,7 @@ int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) { #ifdef _WIN64 FILETIME filetime; - GetSystemTimeAsFileTime(&filetime); + GetSystemTimePreciseAsFileTime(&filetime); constexpr u64 UNIX_TIME_START = 0x295E9648864000; constexpr u64 TICKS_PER_SECOND = 1000000; diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 5b6c17b10..caa254fd8 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -11,7 +11,9 @@ #include "core/libraries/dialogs/error_dialog.h" #include "core/libraries/dialogs/ime_dialog.h" #include "core/libraries/disc_map/disc_map.h" +#include "core/libraries/game_live_streaming/gamelivestreaming.h" #include "core/libraries/gnmdriver/gnmdriver.h" +#include "core/libraries/ime/ime.h" #include "core/libraries/kernel/libkernel.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libpng/pngdec.h" @@ -26,10 +28,12 @@ #include "core/libraries/pad/pad.h" #include "core/libraries/playgo/playgo.h" #include "core/libraries/random/random.h" +#include "core/libraries/remote_play/remoteplay.h" #include "core/libraries/rtc/rtc.h" #include "core/libraries/save_data/dialog/savedatadialog.h" #include "core/libraries/save_data/savedata.h" #include "core/libraries/screenshot/screenshot.h" +#include "core/libraries/share_play/shareplay.h" #include "core/libraries/system/commondialog.h" #include "core/libraries/system/msgdialog.h" #include "core/libraries/system/posix.h" @@ -77,6 +81,10 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::ImeDialog::RegisterlibSceImeDialog(sym); Libraries::AvPlayer::RegisterlibSceAvPlayer(sym); Libraries::Audio3d::RegisterlibSceAudio3d(sym); + Libraries::Ime::RegisterlibSceIme(sym); + Libraries::GameLiveStreaming::RegisterlibSceGameLiveStreaming(sym); + Libraries::SharePlay::RegisterlibSceSharePlay(sym); + Libraries::Remoteplay::RegisterlibSceRemoteplay(sym); } } // namespace Libraries diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index 35d5851ea..161fc5214 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -734,9 +734,16 @@ int PS4_SYSV_ABI sceNetInetNtopWithScopeId() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNetInetPton() { - LOG_ERROR(Lib_Net, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNetInetPton(int af, const char* src, void* dst) { +#ifdef WIN32 + int res = InetPtonA(af, src, dst); +#else + int res = inet_pton(af, src, dst); +#endif + if (res < 0) { + UNREACHABLE(); + } + return res; } int PS4_SYSV_ABI sceNetInetPtonEx() { diff --git a/src/core/libraries/network/net.h b/src/core/libraries/network/net.h index 3f010f685..beef38b56 100644 --- a/src/core/libraries/network/net.h +++ b/src/core/libraries/network/net.h @@ -169,7 +169,7 @@ u64 PS4_SYSV_ABI sceNetHtonll(u64 host64); u16 PS4_SYSV_ABI sceNetHtons(u16 host16); const char* PS4_SYSV_ABI sceNetInetNtop(int af, const void* src, char* dst, u32 size); int PS4_SYSV_ABI sceNetInetNtopWithScopeId(); -int PS4_SYSV_ABI sceNetInetPton(); +int PS4_SYSV_ABI sceNetInetPton(int af, const char* src, void* dst); int PS4_SYSV_ABI sceNetInetPtonEx(); int PS4_SYSV_ABI sceNetInetPtonWithScopeId(); int PS4_SYSV_ABI sceNetInfoDumpStart(); diff --git a/src/core/libraries/network/net_ctl_codes.h b/src/core/libraries/network/net_ctl_codes.h index a38565c1e..0dac4201e 100644 --- a/src/core/libraries/network/net_ctl_codes.h +++ b/src/core/libraries/network/net_ctl_codes.h @@ -26,3 +26,11 @@ constexpr int ORBIS_NET_CTL_STATE_IPOBTAINED = 3; constexpr int ORBIS_NET_CTL_EVENT_TYPE_DISCONNECTED = 1; constexpr int ORBIS_SCE_NET_CTL_EVENT_TYPE_DISCONNECT_REQ_FINISHED = 2; constexpr int ORBIS_NET_CTL_EVENT_TYPE_IPOBTAINED = 3; + +// get info codes +// device +constexpr int ORBIS_NET_CTL_DEVICE_WIRED = 0; +constexpr int ORBIS_NET_CTL_DEVICE_WIRELESS = 1; +// link +constexpr int ORBIS_NET_CTL_LINK_DISCONNECTED = 0; +constexpr int ORBIS_NET_CTL_LINK_CONNECTED = 1; diff --git a/src/core/libraries/network/net_obj.cpp b/src/core/libraries/network/net_obj.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/core/libraries/network/net_obj.h b/src/core/libraries/network/net_obj.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/core/libraries/network/netctl.cpp b/src/core/libraries/network/netctl.cpp index 3ecdde773..e7a69611e 100644 --- a/src/core/libraries/network/netctl.cpp +++ b/src/core/libraries/network/netctl.cpp @@ -1,6 +1,17 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#ifdef WIN32 +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#include +#include +#include +#else +#include +#include +#include +#endif + #include "common/logging/log.h" #include "common/singleton.h" #include "core/libraries/error_codes.h" @@ -149,15 +160,32 @@ int PS4_SYSV_ABI sceNetCtlGetIfStat() { int PS4_SYSV_ABI sceNetCtlGetInfo(int code, OrbisNetCtlInfo* info) { switch (code) { case ORBIS_NET_CTL_INFO_DEVICE: - info->device = 0; + info->device = ORBIS_NET_CTL_DEVICE_WIRED; break; case ORBIS_NET_CTL_INFO_LINK: - info->link = 0; // disconnected + info->link = ORBIS_NET_CTL_LINK_DISCONNECTED; break; + case ORBIS_NET_CTL_INFO_IP_ADDRESS: { + strcpy(info->ip_address, + "127.0.0.1"); // placeholder in case gethostbyname can't find another ip + char devname[80]; + gethostname(devname, 80); + struct hostent* resolved = gethostbyname(devname); + for (int i = 0; resolved->h_addr_list[i] != nullptr; ++i) { + struct in_addr addrIn; + memcpy(&addrIn, resolved->h_addr_list[i], sizeof(u32)); + char* addr = inet_ntoa(addrIn); + if (strcmp(addr, "127.0.0.1") != 0) { + strcpy(info->ip_address, addr); + break; + } + } + break; + } default: LOG_ERROR(Lib_NetCtl, "{} unsupported code", code); } - LOG_ERROR(Lib_NetCtl, "(STUBBED) called"); + LOG_DEBUG(Lib_NetCtl, "(STUBBED) called"); return ORBIS_OK; } @@ -187,7 +215,10 @@ int PS4_SYSV_ABI sceNetCtlGetNetEvConfigInfoIpcInt() { } int PS4_SYSV_ABI sceNetCtlGetResult(int eventType, int* errorCode) { - LOG_ERROR(Lib_NetCtl, "(STUBBED) called eventType = {} ", eventType); + if (!errorCode) { + return ORBIS_NET_CTL_ERROR_INVALID_ADDR; + } + LOG_DEBUG(Lib_NetCtl, "(STUBBED) called eventType = {} ", eventType); *errorCode = 0; return ORBIS_OK; } diff --git a/src/core/libraries/network/netctl.h b/src/core/libraries/network/netctl.h index 850650f97..89ba34c31 100644 --- a/src/core/libraries/network/netctl.h +++ b/src/core/libraries/network/netctl.h @@ -50,6 +50,7 @@ typedef union OrbisNetCtlInfo { // GetInfo codes constexpr int ORBIS_NET_CTL_INFO_DEVICE = 1; constexpr int ORBIS_NET_CTL_INFO_LINK = 4; +constexpr int ORBIS_NET_CTL_INFO_IP_ADDRESS = 14; int PS4_SYSV_ABI sceNetBweCheckCallbackIpcInt(); int PS4_SYSV_ABI sceNetBweClearEventIpcInt(); diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index 28d28cc93..e1aaee814 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -902,12 +902,13 @@ int PS4_SYSV_ABI sceNpCreateAsyncRequest() { } int PS4_SYSV_ABI sceNpCreateRequest() { - LOG_ERROR(Lib_NpManager, "(STUBBED) called"); - return ORBIS_OK; + LOG_ERROR(Lib_NpManager, "(DUMMY) called"); + static int id = 0; + return ++id; } -int PS4_SYSV_ABI sceNpDeleteRequest() { - LOG_ERROR(Lib_NpManager, "(STUBBED) called"); +int PS4_SYSV_ABI sceNpDeleteRequest(int reqId) { + LOG_ERROR(Lib_NpManager, "(DUMMY) called reqId = {}", reqId); return ORBIS_OK; } diff --git a/src/core/libraries/np_manager/np_manager.h b/src/core/libraries/np_manager/np_manager.h index 43ea49ce4..861d91e39 100644 --- a/src/core/libraries/np_manager/np_manager.h +++ b/src/core/libraries/np_manager/np_manager.h @@ -218,7 +218,7 @@ int PS4_SYSV_ABI sceNpCheckNpReachability(); int PS4_SYSV_ABI sceNpCheckPlus(); int PS4_SYSV_ABI sceNpCreateAsyncRequest(); int PS4_SYSV_ABI sceNpCreateRequest(); -int PS4_SYSV_ABI sceNpDeleteRequest(); +int PS4_SYSV_ABI sceNpDeleteRequest(int reqId); int PS4_SYSV_ABI sceNpGetAccountAge(); int PS4_SYSV_ABI sceNpGetAccountCountry(); int PS4_SYSV_ABI sceNpGetAccountCountryA(); diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index b671e0077..d786647c2 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -88,7 +88,7 @@ int PS4_SYSV_ABI scePadGetCapability() { } int PS4_SYSV_ABI scePadGetControllerInformation(s32 handle, OrbisPadControllerInformation* pInfo) { - LOG_INFO(Lib_Pad, "called handle = {}", handle); + LOG_DEBUG(Lib_Pad, "called handle = {}", handle); if (handle < 0) { pInfo->touchPadInfo.pixelDensity = 1; pInfo->touchPadInfo.resolution.x = 1920; diff --git a/src/core/libraries/remote_play/remoteplay.cpp b/src/core/libraries/remote_play/remoteplay.cpp new file mode 100644 index 000000000..b7402173e --- /dev/null +++ b/src/core/libraries/remote_play/remoteplay.cpp @@ -0,0 +1,309 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "remoteplay.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" + +namespace Libraries::Remoteplay { + +int PS4_SYSV_ABI sceRemoteplayApprove() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayChangeEnterKey() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayClearAllRegistData() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayClearConnectHistory() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayConfirmDeviceRegist() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayDisconnect() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGeneratePinCode() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetApMode() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetConnectHistory() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetConnectionStatus(s32 userId, int* pStatus) { + *pStatus = ORBIS_REMOTEPLAY_CONNECTION_STATUS_DISCONNECT; + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetConnectUserId() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetMbusDeviceInfo() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetOperationStatus() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetRemoteplayStatus() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayGetRpMode() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeClose() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeFilterResult() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeGetEvent() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeNotify() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeNotifyEventResult() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeOpen() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeSetCaret() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayImeSetText() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayInitialize() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayIsRemoteOskReady() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayIsRemotePlaying() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayNotifyMbusDeviceRegistComplete() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayNotifyNpPushWakeup() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayNotifyPinCodeError() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayNotifyUserDelete() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayPrintAllRegistData() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayProhibit() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayProhibitStreaming() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayServerLock() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayServerUnLock() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplaySetApMode() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplaySetLogLevel() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplaySetProhibition() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplaySetProhibitionForVsh() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplaySetRpMode() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceRemoteplayTerminate() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_1D5EE365ED5FADB3() { + LOG_ERROR(Lib_Remoteplay, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceRemoteplay(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("xQeIryTX7dY", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayApprove); + LIB_FUNCTION("IYZ+Mu+8tPo", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayChangeEnterKey); + LIB_FUNCTION("ZYUsJtcAnqA", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayClearAllRegistData); + LIB_FUNCTION("cCheyCbF7qw", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayClearConnectHistory); + LIB_FUNCTION("tPYT-kGbZh8", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayConfirmDeviceRegist); + LIB_FUNCTION("6Lg4BNleJWc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayDisconnect); + LIB_FUNCTION("j98LdSGy4eY", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGeneratePinCode); + LIB_FUNCTION("L+cL-M-DP3w", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetApMode); + LIB_FUNCTION("g4K51cY+PEw", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetConnectHistory); + LIB_FUNCTION("g3PNjYKWqnQ", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetConnectionStatus); + LIB_FUNCTION("3eBNV9A0BUM", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetConnectUserId); + LIB_FUNCTION("ufesWMVX6iU", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetMbusDeviceInfo); + LIB_FUNCTION("DxU4JGh4S2k", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetOperationStatus); + LIB_FUNCTION("n5OxFJEvPlc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetRemoteplayStatus); + LIB_FUNCTION("Cekhs6LSHC0", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayGetRpMode); + LIB_FUNCTION("ig1ocbR7Ptw", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeClose); + LIB_FUNCTION("gV9-8cJPM3I", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeFilterResult); + LIB_FUNCTION("cMk57DZXe6c", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeGetEvent); + LIB_FUNCTION("-gwkQpOCl68", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeNotify); + LIB_FUNCTION("58v9tSlRxc8", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeNotifyEventResult); + LIB_FUNCTION("C3r2zT5ebMg", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeOpen); + LIB_FUNCTION("oB730zwoz0s", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeSetCaret); + LIB_FUNCTION("rOTg1Nljp8w", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayImeSetText); + LIB_FUNCTION("k1SwgkMSOM8", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayInitialize); + LIB_FUNCTION("R8RZC1ZIkzU", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayIsRemoteOskReady); + LIB_FUNCTION("uYhiELUtLgA", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayIsRemotePlaying); + LIB_FUNCTION("d-BBSEq1nfc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayNotifyMbusDeviceRegistComplete); + LIB_FUNCTION("Yytq7NE38R8", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayNotifyNpPushWakeup); + LIB_FUNCTION("Wg-w8xjMZA4", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayNotifyPinCodeError); + LIB_FUNCTION("yheulqylKwI", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayNotifyUserDelete); + LIB_FUNCTION("t5ZvUiZ1hpE", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayPrintAllRegistData); + LIB_FUNCTION("mrNh78tBpmg", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayProhibit); + LIB_FUNCTION("7QLrixwVHcU", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayProhibitStreaming); + LIB_FUNCTION("-ThIlThsN80", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayServerLock); + LIB_FUNCTION("0Z-Pm5rZJOI", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayServerUnLock); + LIB_FUNCTION("xSrhtSLIjOc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplaySetApMode); + LIB_FUNCTION("5-2agAeaE+c", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplaySetLogLevel); + LIB_FUNCTION("Rf0XMVR7xPw", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplaySetProhibition); + LIB_FUNCTION("n4l3FTZtNQM", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplaySetProhibitionForVsh); + LIB_FUNCTION("-BPcEQ1w8xc", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplaySetRpMode); + LIB_FUNCTION("BOwybKVa3Do", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + sceRemoteplayTerminate); + LIB_FUNCTION("HV7jZe1frbM", "libSceRemoteplay", 1, "libSceRemoteplay", 0, 0, + Func_1D5EE365ED5FADB3); +}; + +} // namespace Libraries::Remoteplay \ No newline at end of file diff --git a/src/core/libraries/remote_play/remoteplay.h b/src/core/libraries/remote_play/remoteplay.h new file mode 100644 index 000000000..979f73a9e --- /dev/null +++ b/src/core/libraries/remote_play/remoteplay.h @@ -0,0 +1,62 @@ +// 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; +} + +// returning codes in sceRemoteplayGetConnectionStatus pstatus +constexpr int ORBIS_REMOTEPLAY_CONNECTION_STATUS_DISCONNECT = 0; +constexpr int ORBIS_REMOTEPLAY_CONNECTION_STATUS_CONNECT = 1; + +namespace Libraries::Remoteplay { + +int PS4_SYSV_ABI sceRemoteplayApprove(); +int PS4_SYSV_ABI sceRemoteplayChangeEnterKey(); +int PS4_SYSV_ABI sceRemoteplayClearAllRegistData(); +int PS4_SYSV_ABI sceRemoteplayClearConnectHistory(); +int PS4_SYSV_ABI sceRemoteplayConfirmDeviceRegist(); +int PS4_SYSV_ABI sceRemoteplayDisconnect(); +int PS4_SYSV_ABI sceRemoteplayGeneratePinCode(); +int PS4_SYSV_ABI sceRemoteplayGetApMode(); +int PS4_SYSV_ABI sceRemoteplayGetConnectHistory(); +int PS4_SYSV_ABI sceRemoteplayGetConnectionStatus(s32 userId, int* pStatus); +int PS4_SYSV_ABI sceRemoteplayGetConnectUserId(); +int PS4_SYSV_ABI sceRemoteplayGetMbusDeviceInfo(); +int PS4_SYSV_ABI sceRemoteplayGetOperationStatus(); +int PS4_SYSV_ABI sceRemoteplayGetRemoteplayStatus(); +int PS4_SYSV_ABI sceRemoteplayGetRpMode(); +int PS4_SYSV_ABI sceRemoteplayImeClose(); +int PS4_SYSV_ABI sceRemoteplayImeFilterResult(); +int PS4_SYSV_ABI sceRemoteplayImeGetEvent(); +int PS4_SYSV_ABI sceRemoteplayImeNotify(); +int PS4_SYSV_ABI sceRemoteplayImeNotifyEventResult(); +int PS4_SYSV_ABI sceRemoteplayImeOpen(); +int PS4_SYSV_ABI sceRemoteplayImeSetCaret(); +int PS4_SYSV_ABI sceRemoteplayImeSetText(); +int PS4_SYSV_ABI sceRemoteplayInitialize(); +int PS4_SYSV_ABI sceRemoteplayIsRemoteOskReady(); +int PS4_SYSV_ABI sceRemoteplayIsRemotePlaying(); +int PS4_SYSV_ABI sceRemoteplayNotifyMbusDeviceRegistComplete(); +int PS4_SYSV_ABI sceRemoteplayNotifyNpPushWakeup(); +int PS4_SYSV_ABI sceRemoteplayNotifyPinCodeError(); +int PS4_SYSV_ABI sceRemoteplayNotifyUserDelete(); +int PS4_SYSV_ABI sceRemoteplayPrintAllRegistData(); +int PS4_SYSV_ABI sceRemoteplayProhibit(); +int PS4_SYSV_ABI sceRemoteplayProhibitStreaming(); +int PS4_SYSV_ABI sceRemoteplayServerLock(); +int PS4_SYSV_ABI sceRemoteplayServerUnLock(); +int PS4_SYSV_ABI sceRemoteplaySetApMode(); +int PS4_SYSV_ABI sceRemoteplaySetLogLevel(); +int PS4_SYSV_ABI sceRemoteplaySetProhibition(); +int PS4_SYSV_ABI sceRemoteplaySetProhibitionForVsh(); +int PS4_SYSV_ABI sceRemoteplaySetRpMode(); +int PS4_SYSV_ABI sceRemoteplayTerminate(); +int PS4_SYSV_ABI Func_1D5EE365ED5FADB3(); + +void RegisterlibSceRemoteplay(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Remoteplay \ No newline at end of file diff --git a/src/core/libraries/share_play/shareplay.cpp b/src/core/libraries/share_play/shareplay.cpp new file mode 100644 index 000000000..8370438b9 --- /dev/null +++ b/src/core/libraries/share_play/shareplay.cpp @@ -0,0 +1,186 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "shareplay.h" + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" + +namespace Libraries::SharePlay { + +int PS4_SYSV_ABI sceSharePlayCrashDaemon() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayGetCurrentConnectionInfo(OrbisSharePlayConnectionInfo* pInfo) { + memset(pInfo, 0, sizeof(*pInfo)); + pInfo->status = ORBIS_SHARE_PLAY_CONNECTION_STATUS_DORMANT; + LOG_DEBUG(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayGetCurrentConnectionInfoA() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayGetCurrentInfo() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayGetEvent() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayInitialize() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayNotifyDialogOpen() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayNotifyForceCloseForCdlg() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayNotifyOpenQuickMenu() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayResumeScreenForCdlg() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayServerLock() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayServerUnLock() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlaySetMode() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlaySetProhibition() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlaySetProhibitionModeWithAppId() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayStartStandby() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayStartStreaming() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayStopStandby() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayStopStreaming() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI sceSharePlayTerminate() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_2E93C0EA6A6B67C4() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_C1C236728D88E177() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_E9E80C474781F115() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +int PS4_SYSV_ABI Func_F3DD6199DA15ED44() { + LOG_ERROR(Lib_SharePlay, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceSharePlay(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("ggnCfalLU-8", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayCrashDaemon); + LIB_FUNCTION("OOrLKB0bSDs", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayGetCurrentConnectionInfo); + LIB_FUNCTION("+MCXJlWdi+s", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayGetCurrentConnectionInfoA); + LIB_FUNCTION("vUMkWXQff3w", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayGetCurrentInfo); + LIB_FUNCTION("Md7Mdkr8LBc", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayGetEvent); + LIB_FUNCTION("isruqthpYcw", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayInitialize); + LIB_FUNCTION("9zwJpai7jGc", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayNotifyDialogOpen); + LIB_FUNCTION("VUW2V9cUTP4", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayNotifyForceCloseForCdlg); + LIB_FUNCTION("XL0WwUJoQPg", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayNotifyOpenQuickMenu); + LIB_FUNCTION("6-1fKaa5HlY", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayResumeScreenForCdlg); + LIB_FUNCTION("U28jAuLHj6c", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayServerLock); + LIB_FUNCTION("3Oaux9ITEtY", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayServerUnLock); + LIB_FUNCTION("QZy+KmyqKPU", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, sceSharePlaySetMode); + LIB_FUNCTION("co2NCj--pnc", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlaySetProhibition); + LIB_FUNCTION("KADsbjNCgPo", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlaySetProhibitionModeWithAppId); + LIB_FUNCTION("-F6NddfUsa4", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayStartStandby); + LIB_FUNCTION("rWVNHNnEx6g", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayStartStreaming); + LIB_FUNCTION("zEDkUWLVwFI", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayStopStandby); + LIB_FUNCTION("aGlema+JxUU", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayStopStreaming); + LIB_FUNCTION("UaLjloJinow", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + sceSharePlayTerminate); + LIB_FUNCTION("LpPA6mprZ8Q", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + Func_2E93C0EA6A6B67C4); + LIB_FUNCTION("wcI2co2I4Xc", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + Func_C1C236728D88E177); + LIB_FUNCTION("6egMR0eB8RU", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + Func_E9E80C474781F115); + LIB_FUNCTION("891hmdoV7UQ", "libSceSharePlay", 1, "libSceSharePlay", 0, 0, + Func_F3DD6199DA15ED44); + LIB_FUNCTION("OOrLKB0bSDs", "libSceSharePlayCompat", 1, "libSceSharePlay", 0, 0, + sceSharePlayGetCurrentConnectionInfo); +}; + +} // namespace Libraries::SharePlay \ No newline at end of file diff --git a/src/core/libraries/share_play/shareplay.h b/src/core/libraries/share_play/shareplay.h new file mode 100644 index 000000000..8b1ad5f47 --- /dev/null +++ b/src/core/libraries/share_play/shareplay.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::SharePlay { + +constexpr int ORBIS_SHARE_PLAY_CONNECTION_STATUS_DORMANT = 0x00; +constexpr int ORBIS_SHARE_PLAY_CONNECTION_STATUS_READY = 0x01; +constexpr int ORBIS_SHARE_PLAY_CONNECTION_STATUS_CONNECTED = 0x02; + +struct OrbisSharePlayConnectionInfo { + int status; + int mode; + Libraries::NpManager::OrbisNpOnlineId hostOnlineId; + Libraries::NpManager::OrbisNpOnlineId visitorOnlineId; + s32 hostUserId; + s32 visitorUserId; +}; + +int PS4_SYSV_ABI sceSharePlayCrashDaemon(); +int PS4_SYSV_ABI sceSharePlayGetCurrentConnectionInfo(OrbisSharePlayConnectionInfo* pInfo); +int PS4_SYSV_ABI sceSharePlayGetCurrentConnectionInfoA(); +int PS4_SYSV_ABI sceSharePlayGetCurrentInfo(); +int PS4_SYSV_ABI sceSharePlayGetEvent(); +int PS4_SYSV_ABI sceSharePlayInitialize(); +int PS4_SYSV_ABI sceSharePlayNotifyDialogOpen(); +int PS4_SYSV_ABI sceSharePlayNotifyForceCloseForCdlg(); +int PS4_SYSV_ABI sceSharePlayNotifyOpenQuickMenu(); +int PS4_SYSV_ABI sceSharePlayResumeScreenForCdlg(); +int PS4_SYSV_ABI sceSharePlayServerLock(); +int PS4_SYSV_ABI sceSharePlayServerUnLock(); +int PS4_SYSV_ABI sceSharePlaySetMode(); +int PS4_SYSV_ABI sceSharePlaySetProhibition(); +int PS4_SYSV_ABI sceSharePlaySetProhibitionModeWithAppId(); +int PS4_SYSV_ABI sceSharePlayStartStandby(); +int PS4_SYSV_ABI sceSharePlayStartStreaming(); +int PS4_SYSV_ABI sceSharePlayStopStandby(); +int PS4_SYSV_ABI sceSharePlayStopStreaming(); +int PS4_SYSV_ABI sceSharePlayTerminate(); +int PS4_SYSV_ABI Func_2E93C0EA6A6B67C4(); +int PS4_SYSV_ABI Func_C1C236728D88E177(); +int PS4_SYSV_ABI Func_E9E80C474781F115(); +int PS4_SYSV_ABI Func_F3DD6199DA15ED44(); + +void RegisterlibSceSharePlay(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::SharePlay \ No newline at end of file diff --git a/src/core/libraries/system/userservice.cpp b/src/core/libraries/system/userservice.cpp index 560855474..140ca8921 100644 --- a/src/core/libraries/system/userservice.cpp +++ b/src/core/libraries/system/userservice.cpp @@ -491,7 +491,7 @@ int PS4_SYSV_ABI sceUserServiceGetImeRunCount() { } s32 PS4_SYSV_ABI sceUserServiceGetInitialUser(int* user_id) { - LOG_INFO(Lib_UserService, "called"); + LOG_DEBUG(Lib_UserService, "called"); if (user_id == nullptr) { LOG_ERROR(Lib_UserService, "user_id is null"); return ORBIS_USER_SERVICE_ERROR_INVALID_ARGUMENT; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index dd63522fc..644f9389f 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -245,6 +245,7 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) new_vma.is_exec = false; new_vma.phys_base = 0; + rasterizer->MapMemory(mapped_addr, size); return ORBIS_OK; } diff --git a/src/emulator.cpp b/src/emulator.cpp index f69be22ec..3a6c936b9 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -8,6 +8,7 @@ #include "common/logging/backend.h" #include "common/logging/log.h" #ifdef ENABLE_QT_GUI +#include #include "common/memory_patcher.h" #endif #include "common/assert.h" @@ -25,6 +26,7 @@ #include "core/file_format/trp.h" #include "core/file_sys/fs.h" #include "core/libraries/disc_map/disc_map.h" +#include "core/libraries/fiber/fiber.h" #include "core/libraries/kernel/thread_management.h" #include "core/libraries/libc_internal/libc_internal.h" #include "core/libraries/libs.h" @@ -79,6 +81,17 @@ Emulator::Emulator() { // Load renderdoc module. VideoCore::LoadRenderDoc(); + + // Start the timer (Play Time) +#ifdef ENABLE_QT_GUI + start_time = std::chrono::steady_clock::now(); + const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); + QString filePath = QString::fromStdString((user_dir / "play_time.txt").string()); + QFile file(filePath); + if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { + LOG_INFO(Loader, "Error opening or creating play_time.txt"); + } +#endif } Emulator::~Emulator() { @@ -122,6 +135,14 @@ void Emulator::Run(const std::filesystem::path& file) { } #ifdef ENABLE_QT_GUI MemoryPatcher::g_game_serial = id; + + // Timer for 'Play Time' + QTimer* timer = new QTimer(); + QObject::connect(timer, &QTimer::timeout, [this, id]() { + UpdatePlayTime(id); + start_time = std::chrono::steady_clock::now(); + }); + timer->start(60000); // 60000 ms = 1 minute #endif title = param_sfo->GetString("TITLE").value_or("Unknown title"); LOG_INFO(Loader, "Game id: {} Title: {}", id, title); @@ -228,6 +249,10 @@ void Emulator::Run(const std::filesystem::path& file) { window->waitEvent(); } +#ifdef ENABLE_QT_GUI + UpdatePlayTime(id); +#endif + std::exit(0); } @@ -242,7 +267,7 @@ void Emulator::Run(int& argc, char* argv[]) { void Emulator::LoadSystemModules(const std::filesystem::path& file) { constexpr std::array ModulesToLoad{ {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, - {"libSceFiber.sprx", nullptr}, + {"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber}, {"libSceUlt.sprx", nullptr}, {"libSceJson.sprx", nullptr}, {"libSceJson2.sprx", nullptr}, @@ -277,4 +302,74 @@ void Emulator::LoadSystemModules(const std::filesystem::path& file) { } } +#ifdef ENABLE_QT_GUI +void Emulator::UpdatePlayTime(const std::string& serial) { + const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); + QString filePath = QString::fromStdString((user_dir / "play_time.txt").string()); + + QFile file(filePath); + if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { + LOG_INFO(Loader, "Error opening play_time.txt"); + return; + } + + auto end_time = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast(end_time - start_time); + int totalSeconds = duration.count(); + + QTextStream in(&file); + QStringList lines; + QString content; + while (!in.atEnd()) { + content += in.readLine() + "\n"; + } + file.close(); + + QStringList existingLines = content.split('\n', Qt::SkipEmptyParts); + int accumulatedSeconds = 0; + bool found = false; + + for (const QString& line : existingLines) { + QStringList parts = line.split(' '); + if (parts.size() == 2 && parts[0] == QString::fromStdString(serial)) { + QStringList timeParts = parts[1].split(':'); + if (timeParts.size() == 3) { + int hours = timeParts[0].toInt(); + int minutes = timeParts[1].toInt(); + int seconds = timeParts[2].toInt(); + accumulatedSeconds = hours * 3600 + minutes * 60 + seconds; + found = true; + break; + } + } + } + accumulatedSeconds += totalSeconds; + int hours = accumulatedSeconds / 3600; + int minutes = (accumulatedSeconds % 3600) / 60; + int seconds = accumulatedSeconds % 60; + QString playTimeSaved = QString::number(hours) + ":" + + QString::number(minutes).rightJustified(2, '0') + ":" + + QString::number(seconds).rightJustified(2, '0'); + + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream out(&file); + bool lineUpdated = false; + + for (const QString& line : existingLines) { + if (line.startsWith(QString::fromStdString(serial))) { + out << QString::fromStdString(serial) + " " + playTimeSaved + "\n"; + lineUpdated = true; + } else { + out << line << "\n"; + } + } + + if (!lineUpdated) { + out << QString::fromStdString(serial) + " " + playTimeSaved + "\n"; + } + } + LOG_INFO(Loader, "Playing time for {}: {}", serial, playTimeSaved.toStdString()); +} +#endif + } // namespace Core diff --git a/src/emulator.h b/src/emulator.h index 9d40abcd3..9a8bbf6e8 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -27,6 +27,7 @@ public: void Run(const std::filesystem::path& file); void Run(int& argc, char* argv[]); + void UpdatePlayTime(const std::string& serial); private: void LoadSystemModules(const std::filesystem::path& file); @@ -35,6 +36,7 @@ private: Input::GameController* controller; Core::Linker* linker; std::unique_ptr window; + std::chrono::steady_clock::time_point start_time; }; } // namespace Core diff --git a/src/imgui/imgui_config.h b/src/imgui/imgui_config.h index 2094d56bc..ccb084d94 100644 --- a/src/imgui/imgui_config.h +++ b/src/imgui/imgui_config.h @@ -21,7 +21,6 @@ extern void assert_fail_debug_msg(const char* msg); } \ }()) -#define IMGUI_USE_WCHAR32 #define IMGUI_ENABLE_STB_TRUETYPE #define IMGUI_DEFINE_MATH_OPERATORS @@ -29,4 +28,8 @@ extern void assert_fail_debug_msg(const char* msg); constexpr ImVec2(float _v) : x(_v), y(_v) {} #define IM_VEC4_CLASS_EXTRA \ - constexpr ImVec4(float _v) : x(_v), y(_v), z(_v), w(_v) {} \ No newline at end of file + constexpr ImVec4(float _v) : x(_v), y(_v), z(_v), w(_v) {} + +#ifdef IMGUI_USE_WCHAR32 +#error "This project uses 16 bits wchar standard like Orbis" +#endif \ No newline at end of file diff --git a/src/qt_gui/game_info.cpp b/src/qt_gui/game_info.cpp index 6e8d89713..d82f43f20 100644 --- a/src/qt_gui/game_info.cpp +++ b/src/qt_gui/game_info.cpp @@ -10,14 +10,16 @@ GameInfoClass::GameInfoClass() = default; GameInfoClass::~GameInfoClass() = default; void GameInfoClass::GetGameInfo(QWidget* parent) { - QString installDir; - Common::FS::PathToQString(installDir, Config::getGameInstallDir()); QStringList filePaths; - QDir parentFolder(installDir); - QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); - for (const auto& fileInfo : fileList) { - if (fileInfo.isDir()) { - filePaths.append(fileInfo.absoluteFilePath()); + for (const auto& installLoc : Config::getGameInstallDirs()) { + QString installDir; + Common::FS::PathToQString(installDir, installLoc); + QDir parentFolder(installDir); + QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const auto& fileInfo : fileList) { + if (fileInfo.isDir()) { + filePaths.append(fileInfo.absoluteFilePath()); + } } } m_games = QtConcurrent::mapped(filePaths, [&](const QString& path) { diff --git a/src/qt_gui/game_info.h b/src/qt_gui/game_info.h index 9db25482a..8f65803bf 100644 --- a/src/qt_gui/game_info.h +++ b/src/qt_gui/game_info.h @@ -60,6 +60,9 @@ public: if (auto app_ver = psf.GetString("APP_VER"); app_ver.has_value()) { game.version = *app_ver; } + if (const auto play_time = psf.GetString("PLAY_TIME"); play_time.has_value()) { + game.play_time = *play_time; + } } return game; } diff --git a/src/qt_gui/game_install_dialog.cpp b/src/qt_gui/game_install_dialog.cpp index 11daf2de0..e53c58315 100644 --- a/src/qt_gui/game_install_dialog.cpp +++ b/src/qt_gui/game_install_dialog.cpp @@ -51,7 +51,9 @@ QWidget* GameInstallDialog::SetupGamesDirectory() { // Input. m_gamesDirectory = new QLineEdit(); QString install_dir; - Common::FS::PathToQString(install_dir, Config::getGameInstallDir()); + std::filesystem::path install_path = + Config::getGameInstallDirs().empty() ? "" : Config::getGameInstallDirs().front(); + Common::FS::PathToQString(install_dir, install_path); m_gamesDirectory->setText(install_dir); m_gamesDirectory->setMinimumWidth(400); @@ -124,8 +126,7 @@ void GameInstallDialog::Save() { return; } } - - Config::setGameInstallDir(Common::FS::PathFromQString(gamesDirectory)); + Config::addGameInstallDir(Common::FS::PathFromQString(gamesDirectory)); Config::setAddonInstallDir(Common::FS::PathFromQString(addonsDirectory)); const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); Config::save(config_dir / "config.toml"); diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp index c2f6736b8..99628b083 100644 --- a/src/qt_gui/game_list_frame.cpp +++ b/src/qt_gui/game_list_frame.cpp @@ -24,16 +24,17 @@ GameListFrame::GameListFrame(std::shared_ptr game_info_get, QWidg this->horizontalHeader()->setSortIndicatorShown(true); this->horizontalHeader()->setStretchLastSection(true); this->setContextMenuPolicy(Qt::CustomContextMenu); - this->setColumnCount(8); + this->setColumnCount(9); this->setColumnWidth(1, 300); // Name this->setColumnWidth(2, 120); // Serial this->setColumnWidth(3, 90); // Region this->setColumnWidth(4, 90); // Firmware this->setColumnWidth(5, 90); // Size this->setColumnWidth(6, 90); // Version + this->setColumnWidth(7, 100); // Play Time QStringList headers; headers << tr("Icon") << tr("Name") << tr("Serial") << tr("Region") << tr("Firmware") - << tr("Size") << tr("Version") << tr("Path"); + << tr("Size") << tr("Version") << tr("Play Time") << tr("Path"); this->setHorizontalHeaderLabels(headers); this->horizontalHeader()->setSortIndicatorShown(true); this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); @@ -100,9 +101,37 @@ void GameListFrame::PopulateGameList() { SetTableItem(i, 4, QString::fromStdString(m_game_info->m_games[i].fw)); SetTableItem(i, 5, QString::fromStdString(m_game_info->m_games[i].size)); SetTableItem(i, 6, QString::fromStdString(m_game_info->m_games[i].version)); + + QString playTime = GetPlayTime(m_game_info->m_games[i].serial); + if (playTime.isEmpty()) { + m_game_info->m_games[i].play_time = "0:00:00"; + SetTableItem(i, 7, "0"); + } else { + QStringList timeParts = playTime.split(':'); + int hours = timeParts[0].toInt(); + int minutes = timeParts[1].toInt(); + int seconds = timeParts[2].toInt(); + + QString formattedPlayTime; + if (hours > 0) { + formattedPlayTime += QString("%1h ").arg(hours); + } + if (minutes > 0) { + formattedPlayTime += QString("%1m ").arg(minutes); + } + + formattedPlayTime = formattedPlayTime.trimmed(); + m_game_info->m_games[i].play_time = playTime.toStdString(); + if (formattedPlayTime.isEmpty()) { + SetTableItem(i, 7, "0"); + } else { + SetTableItem(i, 7, formattedPlayTime); + } + } + QString path; Common::FS::PathToQString(path, m_game_info->m_games[i].path); - SetTableItem(i, 7, path); + SetTableItem(i, 8, path); } } @@ -171,7 +200,7 @@ void GameListFrame::ResizeIcons(int iconSize) { this->setItem(index, 0, iconItem); index++; } - this->horizontalHeader()->setSectionResizeMode(7, QHeaderView::ResizeToContents); + this->horizontalHeader()->setSectionResizeMode(8, QHeaderView::ResizeToContents); } void GameListFrame::SetTableItem(int row, int column, QString itemStr) { @@ -224,3 +253,33 @@ void GameListFrame::SetRegionFlag(int row, int column, QString itemStr) { this->setItem(row, column, item); this->setCellWidget(row, column, widget); } + +QString GameListFrame::GetPlayTime(const std::string& serial) { + QString playTime; + const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir); + QString filePath = QString::fromStdString((user_dir / "play_time.txt").string()); + + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + return playTime; + } + + while (!file.atEnd()) { + QByteArray line = file.readLine(); + QString lineStr = QString::fromUtf8(line).trimmed(); + + QStringList parts = lineStr.split(' '); + if (parts.size() >= 2) { + QString fileSerial = parts[0]; + QString time = parts[1]; + + if (fileSerial == QString::fromStdString(serial)) { + playTime = time; + break; + } + } + } + + file.close(); + return playTime; +} diff --git a/src/qt_gui/game_list_frame.h b/src/qt_gui/game_list_frame.h index 957ac7318..6da2734a8 100644 --- a/src/qt_gui/game_list_frame.h +++ b/src/qt_gui/game_list_frame.h @@ -29,6 +29,7 @@ public Q_SLOTS: private: void SetTableItem(int row, int column, QString itemStr); void SetRegionFlag(int row, int column, QString itemStr); + QString GetPlayTime(const std::string& serial); QList m_columnActs; GameInfoClass* game_inf_get = nullptr; bool ListSortedAsc = true; @@ -68,6 +69,8 @@ public: case 6: return a.version < b.version; case 7: + return a.play_time < b.play_time; + case 8: return a.path < b.path; default: return false; @@ -89,9 +92,11 @@ public: case 6: return a.version > b.version; case 7: + return a.play_time > b.play_time; + case 8: return a.path > b.path; default: return false; } } -}; \ No newline at end of file +}; diff --git a/src/qt_gui/game_list_utils.h b/src/qt_gui/game_list_utils.h index f62275eff..3d710c5b7 100644 --- a/src/qt_gui/game_list_utils.h +++ b/src/qt_gui/game_list_utils.h @@ -19,6 +19,8 @@ struct GameInfo { std::string version = "Unknown"; std::string region = "Unknown"; std::string fw = "Unknown"; + + std::string play_time = "Unknown"; }; class GameListUtils { diff --git a/src/qt_gui/install_dir_select.cpp b/src/qt_gui/install_dir_select.cpp new file mode 100644 index 000000000..e0951b123 --- /dev/null +++ b/src/qt_gui/install_dir_select.cpp @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "install_dir_select.h" + +InstallDirSelect::InstallDirSelect() : selected_dir() { + selected_dir = Config::getGameInstallDirs().empty() ? "" : Config::getGameInstallDirs().front(); + + if (!Config::getGameInstallDirs().empty() && Config::getGameInstallDirs().size() == 1) { + reject(); + } + + auto layout = new QVBoxLayout(this); + + layout->addWidget(SetupInstallDirList()); + layout->addStretch(); + layout->addWidget(SetupDialogActions()); + + setWindowTitle(tr("shadPS4 - Choose directory")); + setWindowIcon(QIcon(":images/shadps4.ico")); +} + +InstallDirSelect::~InstallDirSelect() {} + +QWidget* InstallDirSelect::SetupInstallDirList() { + auto group = new QGroupBox(tr("Select which directory you want to install to.")); + auto vlayout = new QVBoxLayout(); + + auto m_path_list = new QListWidget(); + QList qt_list; + for (const auto& str : Config::getGameInstallDirs()) { + QString installDirPath; + Common::FS::PathToQString(installDirPath, str); + qt_list.append(installDirPath); + } + m_path_list->insertItems(0, qt_list); + m_path_list->setSpacing(1); + + connect(m_path_list, &QListWidget::itemClicked, this, &InstallDirSelect::setSelectedDirectory); + connect(m_path_list, &QListWidget::itemActivated, this, + &InstallDirSelect::setSelectedDirectory); + + vlayout->addWidget(m_path_list); + + group->setLayout(vlayout); + return group; +} + +void InstallDirSelect::setSelectedDirectory(QListWidgetItem* item) { + if (item) { + const auto highlighted_path = Common::FS::PathFromQString(item->text()); + if (!highlighted_path.empty()) { + selected_dir = highlighted_path; + } + } +} + +QWidget* InstallDirSelect::SetupDialogActions() { + auto actions = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + connect(actions, &QDialogButtonBox::accepted, this, &InstallDirSelect::accept); + connect(actions, &QDialogButtonBox::rejected, this, &InstallDirSelect::reject); + + return actions; +} diff --git a/src/qt_gui/install_dir_select.h b/src/qt_gui/install_dir_select.h new file mode 100644 index 000000000..fdadf2fe0 --- /dev/null +++ b/src/qt_gui/install_dir_select.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "common/config.h" +#include "common/path_util.h" + +class QLineEdit; + +class InstallDirSelect final : public QDialog { +public: + InstallDirSelect(); + ~InstallDirSelect(); + + std::filesystem::path getSelectedDirectory() { + return selected_dir; + } + +private slots: + void BrowseGamesDirectory(); + +private: + QWidget* SetupInstallDirList(); + QWidget* SetupDialogActions(); + void setSelectedDirectory(QListWidgetItem* item); + std::filesystem::path selected_dir; +}; diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp index 32b2102aa..eac32724d 100644 --- a/src/qt_gui/main.cpp +++ b/src/qt_gui/main.cpp @@ -29,7 +29,7 @@ int main(int argc, char* argv[]) { bool has_command_line_argument = argc > 1; // Check if the game install directory is set - if (Config::getGameInstallDir().empty() && !has_command_line_argument) { + if (Config::getGameInstallDirs().empty() && !has_command_line_argument) { GameInstallDialog dlg; dlg.exec(); } diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index f93bdb069..025749dd4 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -16,6 +16,7 @@ #include "core/file_format/pkg.h" #include "core/loader.h" #include "game_install_dialog.h" +#include "install_dir_select.h" #include "main_window.h" #include "settings_dialog.h" #include "video_core/renderer_vulkan/vk_instance.h" @@ -58,6 +59,7 @@ bool MainWindow::Init() { this->show(); // load game list LoadGameLists(); + // Check for update CheckUpdateMain(true); auto end = std::chrono::steady_clock::now(); @@ -671,7 +673,10 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int QMessageBox::critical(this, tr("PKG ERROR"), QString::fromStdString(failreason)); return; } - auto extract_path = Config::getGameInstallDir() / pkg.GetTitleID(); + InstallDirSelect ids; + ids.exec(); + auto game_install_dir = ids.getSelectedDirectory(); + auto extract_path = game_install_dir / pkg.GetTitleID(); QString pkgType = QString::fromStdString(pkg.GetPkgFlags()); QString gameDirPath; Common::FS::PathToQString(gameDirPath, extract_path); @@ -820,7 +825,7 @@ void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int connect(&futureWatcher, &QFutureWatcher::finished, this, [=, this]() { if (pkgNum == nPkg) { QString path; - Common::FS::PathToQString(path, Config::getGameInstallDir()); + Common::FS::PathToQString(path, game_install_dir); QMessageBox extractMsgBox(this); extractMsgBox.setWindowTitle(tr("Extraction Finished")); extractMsgBox.setText( diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index efc438455..efc2cd3eb 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -47,8 +47,6 @@ QStringList languageNames = {"Arabic", const QVector languageIndexes = {21, 23, 14, 6, 18, 1, 12, 22, 2, 4, 25, 24, 29, 5, 0, 9, 15, 16, 17, 7, 26, 8, 11, 20, 3, 13, 27, 10, 19, 28}; -QStringList hideCursorStates = {"Never", "Idle", "Always"}; - SettingsDialog::SettingsDialog(std::span physical_devices, QWidget* parent) : QDialog(parent), ui(new Ui::SettingsDialog) { ui->setupUi(this); @@ -69,7 +67,14 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge completer->setCaseSensitivity(Qt::CaseInsensitive); ui->consoleLanguageComboBox->setCompleter(completer); - ui->hideCursorComboBox->addItems(hideCursorStates); + ui->hideCursorComboBox->addItem(tr("Never")); + ui->hideCursorComboBox->addItem(tr("Idle")); + ui->hideCursorComboBox->addItem(tr("Always")); + + ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Left"), "left"); + ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Center"), "center"); + ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Right"), "right"); + ui->backButtonBehaviorComboBox->addItem(tr("None"), "none"); InitializeEmulatorLanguages(); LoadValuesFromConfig(); @@ -102,15 +107,6 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(tr("Restore Defaults")); ui->buttonBox->button(QDialogButtonBox::Close)->setText(tr("Close")); - ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Left"), "left"); - ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Center"), "center"); - ui->backButtonBehaviorComboBox->addItem(tr("Touchpad Right"), "right"); - ui->backButtonBehaviorComboBox->addItem(tr("None"), "none"); - - QString currentBackButtonBehavior = QString::fromStdString(Config::getBackButtonBehavior()); - int index = ui->backButtonBehaviorComboBox->findData(currentBackButtonBehavior); - ui->backButtonBehaviorComboBox->setCurrentIndex(index != -1 ? index : 0); - connect(ui->tabWidgetSettings, &QTabWidget::currentChanged, this, [this]() { ui->buttonBox->button(QDialogButtonBox::Close)->setFocus(); }); @@ -175,14 +171,6 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge rpc->shutdown(); } }); - - connect(ui->backButtonBehaviorComboBox, QOverload::of(&QComboBox::currentIndexChanged), - this, [this](int index) { - if (index >= 0 && index < ui->backButtonBehaviorComboBox->count()) { - QString data = ui->backButtonBehaviorComboBox->itemData(index).toString(); - Config::setBackButtonBehavior(data.toStdString()); - } - }); } // Input TAB @@ -195,6 +183,14 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge connect(ui->idleTimeoutSpinBox, &QSpinBox::valueChanged, this, [](int index) { Config::setCursorHideTimeout(index); }); + + connect(ui->backButtonBehaviorComboBox, QOverload::of(&QComboBox::currentIndexChanged), + this, [this](int index) { + if (index >= 0 && index < ui->backButtonBehaviorComboBox->count()) { + QString data = ui->backButtonBehaviorComboBox->itemData(index).toString(); + Config::setBackButtonBehavior(data.toStdString()); + } + }); } // GPU TAB @@ -220,6 +216,35 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge [](int val) { Config::setNullGpu(val); }); } + // PATH TAB + { + connect(ui->addFolderButton, &QPushButton::clicked, this, [this]() { + const auto config_dir = Config::getGameInstallDirs(); + QString file_path_string = + QFileDialog::getExistingDirectory(this, tr("Directory to install games")); + auto file_path = Common::FS::PathFromQString(file_path_string); + if (!file_path.empty() && Config::addGameInstallDir(file_path)) { + QListWidgetItem* item = new QListWidgetItem(file_path_string); + ui->gameFoldersListWidget->addItem(item); + } + }); + + connect(ui->gameFoldersListWidget, &QListWidget::itemSelectionChanged, this, [this]() { + ui->removeFolderButton->setEnabled( + !ui->gameFoldersListWidget->selectedItems().isEmpty()); + }); + + connect(ui->removeFolderButton, &QPushButton::clicked, this, [this]() { + QListWidgetItem* selected_item = ui->gameFoldersListWidget->currentItem(); + QString item_path_string = selected_item ? selected_item->text() : QString(); + if (!item_path_string.isEmpty()) { + auto file_path = Common::FS::PathFromQString(item_path_string); + Config::removeGameInstallDir(file_path); + delete selected_item; + } + }); + } + // DEBUG TAB { connect(ui->debugDump, &QCheckBox::stateChanged, this, @@ -248,6 +273,11 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->logFilter->installEventFilter(this); ui->updaterGroupBox->installEventFilter(this); ui->GUIgroupBox->installEventFilter(this); + + // Input + ui->cursorGroupBox->installEventFilter(this); + ui->hideCursorGroupBox->installEventFilter(this); + ui->idleTimeoutGroupBox->installEventFilter(this); ui->backButtonBehaviorGroupBox->installEventFilter(this); // Graphics @@ -258,6 +288,12 @@ SettingsDialog::SettingsDialog(std::span physical_devices, QWidge ui->dumpShadersCheckBox->installEventFilter(this); ui->nullGpuCheckBox->installEventFilter(this); + // Paths + ui->gameFoldersGroupBox->installEventFilter(this); + ui->gameFoldersListWidget->installEventFilter(this); + ui->addFolderButton->installEventFilter(this); + ui->removeFolderButton->installEventFilter(this); + // Debug ui->debugDump->installEventFilter(this); ui->vkValidationCheckBox->installEventFilter(this); @@ -308,9 +344,18 @@ void SettingsDialog::LoadValuesFromConfig() { } ui->updateComboBox->setCurrentText(QString::fromStdString(updateChannel)); + for (const auto& dir : Config::getGameInstallDirs()) { + QString path_string; + Common::FS::PathToQString(path_string, dir); + QListWidgetItem* item = new QListWidgetItem(path_string); + ui->gameFoldersListWidget->addItem(item); + } + QString backButtonBehavior = QString::fromStdString(Config::getBackButtonBehavior()); int index = ui->backButtonBehaviorComboBox->findData(backButtonBehavior); ui->backButtonBehaviorComboBox->setCurrentIndex(index != -1 ? index : 0); + + ui->removeFolderButton->setEnabled(!ui->gameFoldersListWidget->selectedItems().isEmpty()); } void SettingsDialog::InitializeEmulatorLanguages() { @@ -384,6 +429,15 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("updaterGroupBox"); } else if (elementName == "GUIgroupBox") { text = tr("GUIgroupBox"); + } + + // Input + if (elementName == "cursorGroupBox") { + text = tr("cursorGroupBox"); + } else if (elementName == "hideCursorGroupBox") { + text = tr("hideCursorGroupBox"); + } else if (elementName == "idleTimeoutGroupBox") { + text = tr("idleTimeoutGroupBox"); } else if (elementName == "backButtonBehaviorGroupBox") { text = tr("backButtonBehaviorGroupBox"); } @@ -403,6 +457,15 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) { text = tr("nullGpuCheckBox"); } + // Path + if (elementName == "gameFoldersGroupBox" || elementName == "gameFoldersListWidget") { + text = tr("gameFoldersBox"); + } else if (elementName == "addFolderButton") { + text = tr("addFolderButton"); + } else if (elementName == "removeFolderButton") { + text = tr("removeFolderButton"); + } + // Debug if (elementName == "debugDump") { text = tr("debugDump"); diff --git a/src/qt_gui/settings_dialog.ui b/src/qt_gui/settings_dialog.ui index ad9cf5cef..9743e51bd 100644 --- a/src/qt_gui/settings_dialog.ui +++ b/src/qt_gui/settings_dialog.ui @@ -12,7 +12,7 @@ 0 0 854 - 570 + 605 @@ -71,7 +71,7 @@ - 1 + 0 @@ -274,6 +274,9 @@ + + QLayout::SizeConstraint::SetDefaultConstraint + 0 @@ -286,8 +289,157 @@ 0 - + + + + 0 + 0 + + + + + 275 + 0 + + + + + 16777215 + 16777215 + + + + Update + + + + 5 + + + 1 + + + 11 + + + 11 + + + + + + 0 + 0 + + + + + 0 + 75 + + + + + 16777215 + 16777215 + + + + Update Channel + + + + 7 + + + 11 + + + 11 + + + 11 + + + 11 + + + + + + 0 + 0 + + + + + Release + + + + + Nightly + + + + + + + + + + + + 0 + 0 + + + + + 197 + 28 + + + + + 16777215 + 16777215 + + + + Check for Updates + + + + + + + + 0 + 0 + + + + + 11 + false + + + + Check for Updates at Startup + + + + + + + + + + + + 0 @@ -296,204 +448,141 @@ - 265 + 0 0 - - Update - - - - - 10 - 130 - 261 - 22 - - - - Check for Updates at Startup - - - - - - 12 - 30 - 241 - 65 - - - - Update Channel - - - - - 12 - 30 - 217 - 28 - - - - - Release - - - - - Nightly - - - - - - - - 25 - 100 - 215 - 24 - - - - Check for Updates - - - - - - - - - - - - - 0 - 0 - - GUI Settings - - - - 10 - 30 - 241 - 92 - + + + 1 - - - - - - 0 - 0 - - - - Play title music - - - - - - - - - - - Volume - - - - - - - Set the volume of the background music. - - - 100 - - - 10 - - - 20 - - - 50 - - - Qt::Orientation::Horizontal - - - false - - - false - - - QSlider::TickPosition::NoTicks - - - 10 - - - - - - - - - + + 11 + + + + + 1 + + + 0 + + + + + + 0 + 0 + + + + Play title music + + + + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed + + + + 20 + 2 + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Volume + + + + + + + Set the volume of the background music. + + + 100 + + + 10 + + + 20 + + + 50 + + + Qt::Orientation::Horizontal + + + false + + + false + + + QSlider::TickPosition::NoTicks + + + 10 + + + + + + + + + + 0 + 61 + + + + + - + - - - - 0 - 0 - + + + Qt::Orientation::Horizontal - - Controller Settings + + + 40 + 20 + - - - - 12 - 30 - 241 - 65 - - - - Back Button Behavior - - - - - 12 - 30 - 217 - 28 - - - - - + @@ -510,18 +599,48 @@ - - + + 7 + + + 0 + + + Cursor + + 0 + + + 11 + + + 11 + + + true + + + + 0 + 0 + + Hide Cursor + + 7 + + + 11 + @@ -533,10 +652,16 @@ true + + + 0 + 0 + + 0 - 85 + 0 @@ -549,19 +674,28 @@ false + + 6 + 70 - 11 + 5 - + + 5 + + + 5 + + true - + 0 0 @@ -620,26 +754,80 @@ - + - - - Qt::Orientation::Horizontal + + + + 0 + 0 + - - - 40 - 20 - + + Controller - + + + 0 + + + 11 + + + 11 + + + + + true + + + + 0 + 0 + + + + + 237 + 0 + + + + Back Button Behavior + + + + 11 + + + + + + + + + + + true + + + + 0 + 0 + + + + + + - + Qt::Orientation::Horizontal @@ -918,6 +1106,76 @@ + + + Paths + + + + + + + + Game Folders + + + + + 0 + 20 + 401 + 331 + + + + + + + 100 + 360 + 80 + 24 + + + + Add... + + + + + + 210 + 360 + 80 + 24 + + + + Remove + + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Preferred + + + + 40 + 20 + + + + + + + + Debug diff --git a/src/qt_gui/translations/ar.ts b/src/qt_gui/translations/ar.ts index ca23620bb..ec03c04b5 100644 --- a/src/qt_gui/translations/ar.ts +++ b/src/qt_gui/translations/ar.ts @@ -1111,6 +1111,11 @@ Path مسار + + + Play Time + وقت اللعب + CheckUpdate diff --git a/src/qt_gui/translations/da_DK.ts b/src/qt_gui/translations/da_DK.ts index 0d81e2357..c7edfb102 100644 --- a/src/qt_gui/translations/da_DK.ts +++ b/src/qt_gui/translations/da_DK.ts @@ -1111,6 +1111,11 @@ Path Sti + + + Play Time + Spilletid + CheckUpdate diff --git a/src/qt_gui/translations/de.ts b/src/qt_gui/translations/de.ts index 9e31afd6e..43e19a37f 100644 --- a/src/qt_gui/translations/de.ts +++ b/src/qt_gui/translations/de.ts @@ -1111,6 +1111,11 @@ Path Pfad + + + Play Time + Spielzeit + CheckUpdate diff --git a/src/qt_gui/translations/el.ts b/src/qt_gui/translations/el.ts index de10fc3a8..8009dfd88 100644 --- a/src/qt_gui/translations/el.ts +++ b/src/qt_gui/translations/el.ts @@ -1111,6 +1111,11 @@ Path Διαδρομή + + + Play Time + Χρόνος παιχνιδιού + CheckUpdate diff --git a/src/qt_gui/translations/en.ts b/src/qt_gui/translations/en.ts index 18c54428f..974045de1 100644 --- a/src/qt_gui/translations/en.ts +++ b/src/qt_gui/translations/en.ts @@ -434,6 +434,41 @@ Log Filter Log Filter + + + Input + Input + + + + Cursor + Cursor + + + + Hide Cursor + Hide Cursor + + + + Hide Cursor Idle Timeout + Hide Cursor Idle Timeout + + + + Input + Input + + + + Controller + Controller + + + + Back Button Behavior + Back Button Behavior + Graphics @@ -534,16 +569,6 @@ Volume Volume - - - Controller Settings - Controller Settings - - - - Back Button Behavior - Back Button Behavior - MainWindow @@ -1033,6 +1058,41 @@ GUIgroupBox Play Title Music:\nIf a game supports it, enable playing special music when selecting the game in the GUI. + + + cursorGroupBox + Cursor:\nChange settings related to the cursor. + + + + hideCursorGroupBox + Hide Cursor:\nSet cursor hiding behavior. + + + + idleTimeoutGroupBox + Hide Idle Cursor Timeout:\nThe duration (seconds) after which the cursor that has been idle hides itself. + + + + Never + Never + + + + Idle + Idle + + + + Always + Always + + + + backButtonBehaviorGroupBox + Back Button Behavior:\nAllows setting which part of the touchpad the back button will emulate a touch on. + backButtonBehaviorGroupBox @@ -1083,6 +1143,21 @@ nullGpuCheckBox Enable Null GPU:\nFor the sake of technical debugging, disables game rendering as if there were no graphics card. + + + gameFoldersBox + Game Folders: The list of folders to check for installed games. + + + + addFolderButton + Add: Add a folder to the list. + + + + removeFolderButton + Remove: Remove a folder from the list. + debugDump @@ -1146,6 +1221,11 @@ Path Path + + + Play Time + Play Time + CheckUpdate diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index 86a3e1adc..0c6591a5f 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -1111,6 +1111,11 @@ Path Ruta + + + Play Time + Tiempo de Juego + CheckUpdate diff --git a/src/qt_gui/translations/fa_IR.ts b/src/qt_gui/translations/fa_IR.ts index a613af70b..60d8be89e 100644 --- a/src/qt_gui/translations/fa_IR.ts +++ b/src/qt_gui/translations/fa_IR.ts @@ -1111,6 +1111,11 @@ Path مسیر + + + Play Time + زمان بازی + CheckUpdate diff --git a/src/qt_gui/translations/fi.ts b/src/qt_gui/translations/fi.ts index e4010b872..30c60af81 100644 --- a/src/qt_gui/translations/fi.ts +++ b/src/qt_gui/translations/fi.ts @@ -1111,6 +1111,11 @@ Path Polku + + + Play Time + Peliaika + CheckUpdate diff --git a/src/qt_gui/translations/fr.ts b/src/qt_gui/translations/fr.ts index b11b585c9..54e4b0e82 100644 --- a/src/qt_gui/translations/fr.ts +++ b/src/qt_gui/translations/fr.ts @@ -1111,6 +1111,11 @@ Path Répertoire + + + Play Time + Temps de jeu + CheckUpdate diff --git a/src/qt_gui/translations/hu_HU.ts b/src/qt_gui/translations/hu_HU.ts index 5986193c4..a6481d5a2 100644 --- a/src/qt_gui/translations/hu_HU.ts +++ b/src/qt_gui/translations/hu_HU.ts @@ -1111,6 +1111,11 @@ Path Útvonal + + + Play Time + Játékidő + CheckUpdate diff --git a/src/qt_gui/translations/id.ts b/src/qt_gui/translations/id.ts index 274029bd0..2fdc9b68c 100644 --- a/src/qt_gui/translations/id.ts +++ b/src/qt_gui/translations/id.ts @@ -1111,6 +1111,11 @@ Path Jalur + + + Play Time + Waktu Bermain + CheckUpdate diff --git a/src/qt_gui/translations/it.ts b/src/qt_gui/translations/it.ts index 07976865c..aba38a290 100644 --- a/src/qt_gui/translations/it.ts +++ b/src/qt_gui/translations/it.ts @@ -1111,6 +1111,11 @@ Path Percorso + + + Play Time + Tempo di Gioco + CheckUpdate diff --git a/src/qt_gui/translations/ja_JP.ts b/src/qt_gui/translations/ja_JP.ts index c3444906b..7524250f5 100644 --- a/src/qt_gui/translations/ja_JP.ts +++ b/src/qt_gui/translations/ja_JP.ts @@ -1111,6 +1111,11 @@ Path パス + + + Play Time + プレイ時間 + CheckUpdate diff --git a/src/qt_gui/translations/ko_KR.ts b/src/qt_gui/translations/ko_KR.ts index e816e2f4b..4805a4d0e 100644 --- a/src/qt_gui/translations/ko_KR.ts +++ b/src/qt_gui/translations/ko_KR.ts @@ -1111,6 +1111,11 @@ Path Path + + + Play Time + Play Time + CheckUpdate diff --git a/src/qt_gui/translations/lt_LT.ts b/src/qt_gui/translations/lt_LT.ts index d5b4ff3ad..beaaa116a 100644 --- a/src/qt_gui/translations/lt_LT.ts +++ b/src/qt_gui/translations/lt_LT.ts @@ -1111,6 +1111,11 @@ Path Kelias + + + Play Time + Žaidimo laikas + CheckUpdate diff --git a/src/qt_gui/translations/nb.ts b/src/qt_gui/translations/nb.ts index 334600950..c193e83dd 100644 --- a/src/qt_gui/translations/nb.ts +++ b/src/qt_gui/translations/nb.ts @@ -1111,6 +1111,11 @@ Path Sti + + + Play Time + Spilletid + CheckUpdate diff --git a/src/qt_gui/translations/nl.ts b/src/qt_gui/translations/nl.ts index 6e49d4640..6b0befa92 100644 --- a/src/qt_gui/translations/nl.ts +++ b/src/qt_gui/translations/nl.ts @@ -1111,6 +1111,11 @@ Path Pad + + + Play Time + Speeltijd + CheckUpdate diff --git a/src/qt_gui/translations/pl_PL.ts b/src/qt_gui/translations/pl_PL.ts index e95b8ba0c..ba8b4f9e4 100644 --- a/src/qt_gui/translations/pl_PL.ts +++ b/src/qt_gui/translations/pl_PL.ts @@ -1111,6 +1111,11 @@ Path Ścieżka + + + Play Time + Czas gry + CheckUpdate diff --git a/src/qt_gui/translations/pt_BR.ts b/src/qt_gui/translations/pt_BR.ts index e1570b2cd..2e859f12b 100644 --- a/src/qt_gui/translations/pt_BR.ts +++ b/src/qt_gui/translations/pt_BR.ts @@ -1111,6 +1111,11 @@ Path Diretório + + + Play Time + Horas Jogadas + CheckUpdate diff --git a/src/qt_gui/translations/ro_RO.ts b/src/qt_gui/translations/ro_RO.ts index e98b7e5c9..b36647c1c 100644 --- a/src/qt_gui/translations/ro_RO.ts +++ b/src/qt_gui/translations/ro_RO.ts @@ -1111,6 +1111,11 @@ Path Drum + + + Play Time + Timp de Joacă + CheckUpdate diff --git a/src/qt_gui/translations/ru_RU.ts b/src/qt_gui/translations/ru_RU.ts index aaff66ef9..224342f34 100644 --- a/src/qt_gui/translations/ru_RU.ts +++ b/src/qt_gui/translations/ru_RU.ts @@ -1111,6 +1111,11 @@ Path Путь + + + Play Time + Время Игры + CheckUpdate diff --git a/src/qt_gui/translations/sq.ts b/src/qt_gui/translations/sq.ts index dddc694a4..062226369 100644 --- a/src/qt_gui/translations/sq.ts +++ b/src/qt_gui/translations/sq.ts @@ -1111,6 +1111,11 @@ Path Shtegu + + + Play Time + Kohë Lojë + CheckUpdate diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 5cd71af34..3e9173c7c 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -1111,6 +1111,11 @@ Path Yol + + + Play Time + Oynama Süresi + CheckUpdate diff --git a/src/qt_gui/translations/vi_VN.ts b/src/qt_gui/translations/vi_VN.ts index 523683621..af8d218a6 100644 --- a/src/qt_gui/translations/vi_VN.ts +++ b/src/qt_gui/translations/vi_VN.ts @@ -1111,6 +1111,11 @@ Path Đường dẫn + + + Play Time + Thời gian chơi + CheckUpdate diff --git a/src/qt_gui/translations/zh_CN.ts b/src/qt_gui/translations/zh_CN.ts index f2abc4f4b..5eef55641 100644 --- a/src/qt_gui/translations/zh_CN.ts +++ b/src/qt_gui/translations/zh_CN.ts @@ -1111,6 +1111,11 @@ Path 路径 + + + Play Time + 游戏时间 + CheckUpdate diff --git a/src/qt_gui/translations/zh_TW.ts b/src/qt_gui/translations/zh_TW.ts index 2d34f8d12..7e3585a07 100644 --- a/src/qt_gui/translations/zh_TW.ts +++ b/src/qt_gui/translations/zh_TW.ts @@ -1111,6 +1111,11 @@ Path 路徑 + + + Play Time + 遊玩時間 + CheckUpdate diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index f90e9db77..e84908a57 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -206,10 +206,7 @@ Id DefineMain(EmitContext& ctx, const IR::Program& program) { return main; } -void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { - const auto& info = program.info; - const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size()); - spv::ExecutionModel execution_model{}; +void SetupCapabilities(const Info& info, EmitContext& ctx) { ctx.AddCapability(spv::Capability::Image1D); ctx.AddCapability(spv::Capability::Sampled1D); ctx.AddCapability(spv::Capability::ImageQuery); @@ -247,6 +244,19 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { if (info.uses_group_ballot) { ctx.AddCapability(spv::Capability::GroupNonUniformBallot); } + if (info.stage == Stage::Export || info.stage == Stage::Vertex) { + ctx.AddExtension("SPV_KHR_shader_draw_parameters"); + ctx.AddCapability(spv::Capability::DrawParameters); + } + if (info.stage == Stage::Geometry) { + ctx.AddCapability(spv::Capability::Geometry); + } +} + +void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { + const auto& info = program.info; + const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size()); + spv::ExecutionModel execution_model{}; switch (program.info.stage) { case Stage::Compute: { const std::array workgroup_size{ctx.runtime_info.cs_info.workgroup_size}; @@ -290,6 +300,24 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { ctx.AddEntryPoint(execution_model, main, "main", interfaces); } +void SetupFloatMode(EmitContext& ctx, const Profile& profile, const RuntimeInfo& runtime_info, + Id main_func) { + ctx.AddExtension("SPV_KHR_float_controls"); + const auto fp_denorm_mode = runtime_info.fp_denorm_mode32; + if (fp_denorm_mode == AmdGpu::FpDenormMode::InOutFlush) { + if (profile.support_fp32_denorm_flush) { + ctx.AddCapability(spv::Capability::DenormFlushToZero); + ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 32U); + } + } else { + LOG_WARNING(Render_Vulkan, "Unknown FP denorm mode {}", u32(fp_denorm_mode)); + } + const auto fp_round_mode = runtime_info.fp_round_mode32; + if (fp_round_mode != AmdGpu::FpRoundMode::NearestEven) { + LOG_WARNING(Render_Vulkan, "Unknown FP rounding mode {}", u32(fp_round_mode)); + } +} + void PatchPhiNodes(const IR::Program& program, EmitContext& ctx) { auto inst{program.blocks.front()->begin()}; size_t block_index{0}; @@ -314,18 +342,8 @@ std::vector EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_in EmitContext ctx{profile, runtime_info, program.info, binding}; const Id main{DefineMain(ctx, program)}; DefineEntryPoint(program, ctx, main); - switch (program.info.stage) { - case Stage::Export: - case Stage::Vertex: - ctx.AddExtension("SPV_KHR_shader_draw_parameters"); - ctx.AddCapability(spv::Capability::DrawParameters); - break; - case Stage::Geometry: - ctx.AddCapability(spv::Capability::Geometry); - break; - default: - break; - } + SetupCapabilities(program.info, ctx); + SetupFloatMode(ctx, profile, runtime_info, main); PatchPhiNodes(program, ctx); binding.user_data += program.info.ud_mask.NumRegs(); return ctx.Assemble(); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 8f062d6e7..fc99b8925 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -59,19 +59,22 @@ struct ImageOperands { } } - void AddDerivatives(EmitContext& ctx, Id derivatives) { - if (!Sirit::ValidId(derivatives)) { + void AddDerivatives(EmitContext& ctx, Id derivatives_dx, Id derivatives_dy) { + if (!Sirit::ValidId(derivatives_dx) || !Sirit::ValidId(derivatives_dy)) { return; } - const Id dx{ctx.OpVectorShuffle(ctx.F32[2], derivatives, derivatives, 0, 1)}; - const Id dy{ctx.OpVectorShuffle(ctx.F32[2], derivatives, derivatives, 2, 3)}; - Add(spv::ImageOperandsMask::Grad, dx, dy); + Add(spv::ImageOperandsMask::Grad, derivatives_dx, derivatives_dy); } spv::ImageOperandsMask mask{}; boost::container::static_vector operands; }; +Id EmitImageSampleRaw(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address1, Id address2, + Id address3, Id address4) { + UNREACHABLE_MSG("Unreachable instruction"); +} + Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id bias, const IR::Value& offset) { const auto& texture = ctx.images[handle & 0xFFFF]; @@ -114,7 +117,9 @@ Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, operands.AddOffset(ctx, offset); const Id sample = ctx.OpImageSampleDrefImplicitLod(result_type, sampled_image, coords, dref, operands.mask, operands.operands); - return texture.is_integer ? ctx.OpBitcast(ctx.F32[1], sample) : sample; + const Id sample_typed = texture.is_integer ? ctx.OpBitcast(ctx.F32[1], sample) : sample; + return ctx.OpCompositeConstruct(ctx.F32[4], sample_typed, ctx.f32_zero_value, + ctx.f32_zero_value, ctx.f32_zero_value); } Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id dref, @@ -129,7 +134,9 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, operands.Add(spv::ImageOperandsMask::Lod, lod); const Id sample = ctx.OpImageSampleDrefExplicitLod(result_type, sampled_image, coords, dref, operands.mask, operands.operands); - return texture.is_integer ? ctx.OpBitcast(ctx.F32[1], sample) : sample; + const Id sample_typed = texture.is_integer ? ctx.OpBitcast(ctx.F32[1], sample) : sample; + return ctx.OpCompositeConstruct(ctx.F32[4], sample_typed, ctx.f32_zero_value, + ctx.f32_zero_value, ctx.f32_zero_value); } Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, @@ -212,15 +219,15 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords) { return ctx.OpImageQueryLod(ctx.F32[2], sampled_image, coords); } -Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id derivatives, - const IR::Value& offset, Id lod_clamp) { +Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id derivatives_dx, + Id derivatives_dy, const IR::Value& offset, const IR::Value& lod_clamp) { const auto& texture = ctx.images[handle & 0xFFFF]; const Id image = ctx.OpLoad(texture.image_type, texture.id); const Id result_type = texture.data_types->Get(4); const Id sampler = ctx.OpLoad(ctx.sampler_type, ctx.samplers[handle >> 16]); const Id sampled_image = ctx.OpSampledImage(texture.sampled_type, image, sampler); ImageOperands operands; - operands.AddDerivatives(ctx, derivatives); + operands.AddDerivatives(ctx, derivatives_dx, derivatives_dy); operands.AddOffset(ctx, offset); const Id sample = ctx.OpImageSampleExplicitLod(result_type, sampled_image, coords, operands.mask, operands.operands); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index 6ae1ef24a..02b98b343 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -368,6 +368,8 @@ Id EmitConvertF64U64(EmitContext& ctx, Id value); Id EmitConvertU16U32(EmitContext& ctx, Id value); Id EmitConvertU32U16(EmitContext& ctx, Id value); +Id EmitImageSampleRaw(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address1, Id address2, + Id address3, Id address4); Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id bias, const IR::Value& offset); Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id lod, @@ -384,8 +386,8 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, const Id lod, Id ms); Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod, bool skip_mips); Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords); -Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id derivatives, - const IR::Value& offset, Id lod_clamp); +Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id derivatives_dx, + Id derivatives_dy, const IR::Value& offset, const IR::Value& lod_clamp); Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id color); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 5eee656dd..6581a7a56 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -284,7 +284,8 @@ void EmitContext::DefineInputs() { frag_coord = DefineVariable(F32[4], spv::BuiltIn::FragCoord, spv::StorageClass::Input); frag_depth = DefineVariable(F32[1], spv::BuiltIn::FragDepth, spv::StorageClass::Output); front_facing = DefineVariable(U1[1], spv::BuiltIn::FrontFacing, spv::StorageClass::Input); - for (const auto& input : runtime_info.fs_info.inputs) { + for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) { + const auto& input = runtime_info.fs_info.inputs[i]; const u32 semantic = input.param_index; ASSERT(semantic < IR::NumParams); if (input.is_default && !input.is_flat) { @@ -333,7 +334,6 @@ void EmitContext::DefineInputs() { const auto num_params = runtime_info.gs_info.in_vertex_data_size / 4 - 1u; for (int param_id = 0; param_id < num_params; ++param_id) { - const IR::Attribute param{IR::Attribute::Param0 + param_id}; const Id type{TypeArray(F32[4], ConstU32(num_verts_in))}; const Id id{DefineInput(type, param_id)}; Name(id, fmt::format("in_attr{}", param_id)); @@ -394,8 +394,7 @@ void EmitContext::DefineOutputs() { case Stage::Geometry: { output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output); - for (u32 attr_id = 0; attr_id < runtime_info.gs_info.copy_data.num_attrs; attr_id++) { - const IR::Attribute param{IR::Attribute::Param0 + attr_id}; + for (u32 attr_id = 0; attr_id < info.gs_copy_data.num_attrs; attr_id++) { const Id id{DefineOutput(F32[4], attr_id)}; Name(id, fmt::format("out_attr{}", attr_id)); output_params[attr_id] = {id, output_f32, F32[1], 4u}; diff --git a/src/shader_recompiler/frontend/copy_shader.cpp b/src/shader_recompiler/frontend/copy_shader.cpp index 363c1c821..b2c795667 100644 --- a/src/shader_recompiler/frontend/copy_shader.cpp +++ b/src/shader_recompiler/frontend/copy_shader.cpp @@ -7,7 +7,7 @@ namespace Shader { -CopyShaderData ParseCopyShader(const std::span& code) { +CopyShaderData ParseCopyShader(std::span code) { Gcn::GcnCodeSlice code_slice{code.data(), code.data() + code.size()}; Gcn::GcnDecodeContext decoder; diff --git a/src/shader_recompiler/frontend/copy_shader.h b/src/shader_recompiler/frontend/copy_shader.h index ca3e1ac3e..55cc31ebd 100644 --- a/src/shader_recompiler/frontend/copy_shader.h +++ b/src/shader_recompiler/frontend/copy_shader.h @@ -16,6 +16,6 @@ struct CopyShaderData { u32 num_attrs{0}; }; -CopyShaderData ParseCopyShader(const std::span& code); +CopyShaderData ParseCopyShader(std::span code); } // namespace Shader diff --git a/src/shader_recompiler/frontend/decode.cpp b/src/shader_recompiler/frontend/decode.cpp index 98f97dd12..796bed127 100644 --- a/src/shader_recompiler/frontend/decode.cpp +++ b/src/shader_recompiler/frontend/decode.cpp @@ -654,7 +654,7 @@ void GcnDecodeContext::decodeInstructionVOP3(uint64_t hexInstruction) { OpcodeVOP3 vop3Op = static_cast(op); if (IsVop3BEncoding(m_instruction.opcode)) { - m_instruction.dst[1].field = OperandField::ScalarGPR; + m_instruction.dst[1].field = getOperandField(sdst); m_instruction.dst[1].type = ScalarType::Uint64; m_instruction.dst[1].code = sdst; } else { diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index c77588280..b70d4b829 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -155,6 +155,8 @@ public: void V_SUB_I32(const GcnInst& inst); void V_SUBREV_I32(const GcnInst& inst); void V_ADDC_U32(const GcnInst& inst); + void V_SUBB_U32(const GcnInst& inst); + void V_SUBBREV_U32(const GcnInst& inst); void V_LDEXP_F32(const GcnInst& inst); void V_CVT_PKNORM_U16_F32(const GcnInst& inst); void V_CVT_PKRTZ_F16_F32(const GcnInst& inst); @@ -273,7 +275,9 @@ private: void SetDst(const InstOperand& operand, const IR::U32F32& value); void SetDst64(const InstOperand& operand, const IR::U64F64& value_raw); - // Vector ALU Helprers + // Vector ALU Helpers + IR::U32 GetCarryIn(const GcnInst& inst); + void SetCarryOut(const GcnInst& inst, const IR::U1& carry); IR::U32 VMovRelSHelper(u32 src_vgprno, const IR::U32 m0); void VMovRelDHelper(u32 dst_vgprno, const IR::U32 src_val, const IR::U32 m0); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index b061d3b78..279695461 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -87,6 +87,10 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_SUBREV_I32(inst); case Opcode::V_ADDC_U32: return V_ADDC_U32(inst); + case Opcode::V_SUBB_U32: + return V_SUBB_U32(inst); + case Opcode::V_SUBBREV_U32: + return V_SUBBREV_U32(inst); case Opcode::V_LDEXP_F32: return V_LDEXP_F32(inst); case Opcode::V_CVT_PKNORM_U16_F32: @@ -546,51 +550,71 @@ void Translator::V_MBCNT_U32_B32(bool is_low, const GcnInst& inst) { } void Translator::V_ADD_I32(const GcnInst& inst) { + // Signed or unsigned components const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{ir.GetVectorReg(IR::VectorReg(inst.src[1].code))}; - SetDst(inst.dst[0], ir.IAdd(src0, src1)); - // TODO: Carry + const IR::U32 result{ir.IAdd(src0, src1)}; + SetDst(inst.dst[0], result); + + // TODO: Carry-out with signed or unsigned components } void Translator::V_SUB_I32(const GcnInst& inst) { + // Unsigned components const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; - SetDst(inst.dst[0], ir.ISub(src0, src1)); + const IR::U32 result{ir.ISub(src0, src1)}; + SetDst(inst.dst[0], result); + + const IR::U1 did_underflow{ir.IGreaterThan(src1, src0, false)}; + SetCarryOut(inst, did_underflow); } void Translator::V_SUBREV_I32(const GcnInst& inst) { + // Unsigned components const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src1{GetSrc(inst.src[1])}; - SetDst(inst.dst[0], ir.ISub(src1, src0)); - // TODO: Carry-out + const IR::U32 result{ir.ISub(src1, src0)}; + SetDst(inst.dst[0], result); + + const IR::U1 did_underflow{ir.IGreaterThan(src0, src1, false)}; + SetCarryOut(inst, did_underflow); } void Translator::V_ADDC_U32(const GcnInst& inst) { - const auto src0 = GetSrc(inst.src[0]); - const auto src1 = GetSrc(inst.src[1]); - - IR::U1 carry; - if (inst.src_count == 3) { // VOP3 - if (inst.src[2].field == OperandField::VccLo) { - carry = ir.GetVcc(); - } else if (inst.src[2].field == OperandField::ScalarGPR) { - carry = ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[2].code)); - } else { - UNREACHABLE(); - } - } else { // VOP2 - carry = ir.GetVcc(); - } - - const IR::U32 scarry = IR::U32{ir.Select(carry, ir.Imm32(1), ir.Imm32(0))}; - const IR::U32 result = ir.IAdd(ir.IAdd(src0, src1), scarry); - + // Unsigned components + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 carry{GetCarryIn(inst)}; + const IR::U32 result{ir.IAdd(ir.IAdd(src0, src1), carry)}; SetDst(inst.dst[0], result); - const IR::U1 less_src0 = ir.ILessThan(result, src0, false); - const IR::U1 less_src1 = ir.ILessThan(result, src1, false); - const IR::U1 did_overflow = ir.LogicalOr(less_src0, less_src1); - ir.SetVcc(did_overflow); + const IR::U1 less_src0{ir.ILessThan(result, src0, false)}; + const IR::U1 less_src1{ir.ILessThan(result, src1, false)}; + const IR::U1 did_overflow{ir.LogicalOr(less_src0, less_src1)}; + SetCarryOut(inst, did_overflow); +} + +void Translator::V_SUBB_U32(const GcnInst& inst) { + // Signed or unsigned components + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 carry{GetCarryIn(inst)}; + const IR::U32 result{ir.ISub(ir.ISub(src0, src1), carry)}; + SetDst(inst.dst[0], result); + + // TODO: Carry-out with signed or unsigned components +} + +void Translator::V_SUBBREV_U32(const GcnInst& inst) { + // Signed or unsigned components + const IR::U32 src0{GetSrc(inst.src[0])}; + const IR::U32 src1{GetSrc(inst.src[1])}; + const IR::U32 carry{GetCarryIn(inst)}; + const IR::U32 result{ir.ISub(ir.ISub(src1, src0), carry)}; + SetDst(inst.dst[0], result); + + // TODO: Carry-out with signed or unsigned components } void Translator::V_LDEXP_F32(const GcnInst& inst) { @@ -1152,6 +1176,37 @@ void Translator::V_MAD_U64_U32(const GcnInst& inst) { ir.SetVcc(did_overflow); } +IR::U32 Translator::GetCarryIn(const GcnInst& inst) { + IR::U1 carry; + if (inst.src_count == 3) { // VOP3 + if (inst.src[2].field == OperandField::VccLo) { + carry = ir.GetVcc(); + } else if (inst.src[2].field == OperandField::ScalarGPR) { + carry = ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[2].code)); + } else { + UNREACHABLE(); + } + } else { // VOP2 + carry = ir.GetVcc(); + } + + return IR::U32{ir.Select(carry, ir.Imm32(1), ir.Imm32(0))}; +} + +void Translator::SetCarryOut(const GcnInst& inst, const IR::U1& carry) { + if (inst.dst_count == 2) { // VOP3 + if (inst.dst[1].field == OperandField::VccLo) { + ir.SetVcc(carry); + } else if (inst.dst[1].field == OperandField::ScalarGPR) { + ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[1].code), carry); + } else { + UNREACHABLE(); + } + } else { // VOP2 + ir.SetVcc(carry); + } +} + // TODO: add range analysis pass to hopefully put an upper bound on m0, and only select one of // [src_vgprno, src_vgprno + max_m0]. Same for dst regs we may write back to diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index e76ba6d8a..b7ad3b36b 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -411,7 +411,7 @@ void Translator::IMAGE_LOAD(bool has_mip, const GcnInst& inst) { ir.GetVectorReg(addr_reg + 2), ir.GetVectorReg(addr_reg + 3)); IR::TextureInstInfo info{}; - info.explicit_lod.Assign(has_mip); + info.has_lod.Assign(has_mip); const IR::Value texel = ir.ImageFetch(handle, body, {}, {}, {}, info); for (u32 i = 0; i < 4; i++) { @@ -513,6 +513,76 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) { } } +IR::Value EmitImageSample(IR::IREmitter& ir, const GcnInst& inst, const IR::ScalarReg tsharp_reg, + const IR::ScalarReg sampler_reg, const IR::VectorReg addr_reg, + bool gather) { + const auto& mimg = inst.control.mimg; + const auto flags = MimgModifierFlags(mimg.mod); + + IR::TextureInstInfo info{}; + info.is_depth.Assign(flags.test(MimgModifier::Pcf)); + info.has_bias.Assign(flags.test(MimgModifier::LodBias)); + info.has_lod_clamp.Assign(flags.test(MimgModifier::LodClamp)); + info.force_level0.Assign(flags.test(MimgModifier::Level0)); + info.has_offset.Assign(flags.test(MimgModifier::Offset)); + info.has_lod.Assign(flags.any(MimgModifier::Lod)); + info.is_array.Assign(mimg.da); + + if (gather) { + info.gather_comp.Assign(std::bit_width(mimg.dmask) - 1); + info.is_gather.Assign(true); + } else { + info.has_derivatives.Assign(flags.test(MimgModifier::Derivative)); + } + + // Load first dword of T# and S#. We will use them as the handle that will guide resource + // tracking pass where to read the sharps. This will later also get patched to the SPIRV texture + // binding index. + const IR::Value handle = + ir.CompositeConstruct(ir.GetScalarReg(tsharp_reg), ir.GetScalarReg(sampler_reg)); + + // Determine how many address registers need to be passed. + // The image type is unknown, so add all 4 possible base registers and resolve later. + int num_addr_regs = 4; + if (info.has_offset) { + ++num_addr_regs; + } + if (info.has_bias) { + ++num_addr_regs; + } + if (info.is_depth) { + ++num_addr_regs; + } + if (info.has_derivatives) { + // The image type is unknown, so add all 6 possible derivative registers and resolve later. + num_addr_regs += 6; + } + + // Fetch all the address registers to pass in the IR instruction. There can be up to 13 + // registers. + const auto get_addr_reg = [&](int index) -> IR::F32 { + if (index >= num_addr_regs) { + return ir.Imm32(0.f); + } + return ir.GetVectorReg(addr_reg + index); + }; + const IR::Value address1 = + ir.CompositeConstruct(get_addr_reg(0), get_addr_reg(1), get_addr_reg(2), get_addr_reg(3)); + const IR::Value address2 = + ir.CompositeConstruct(get_addr_reg(4), get_addr_reg(5), get_addr_reg(6), get_addr_reg(7)); + const IR::Value address3 = + ir.CompositeConstruct(get_addr_reg(8), get_addr_reg(9), get_addr_reg(10), get_addr_reg(11)); + const IR::Value address4 = get_addr_reg(12); + + // Issue the placeholder IR instruction. + IR::Value texel = ir.ImageSampleRaw(handle, address1, address2, address3, address4, info); + if (info.is_depth && !gather) { + // For non-gather depth sampling, only return a single value. + texel = ir.CompositeExtract(texel, 0); + } + return texel; +} + void Translator::IMAGE_SAMPLE(const GcnInst& inst) { const auto& mimg = inst.control.mimg; IR::VectorReg addr_reg{inst.src[0].code}; @@ -521,72 +591,7 @@ void Translator::IMAGE_SAMPLE(const GcnInst& inst) { const IR::ScalarReg sampler_reg{inst.src[3].code * 4}; const auto flags = MimgModifierFlags(mimg.mod); - // Load first dword of T# and S#. We will use them as the handle that will guide resource - // tracking pass where to read the sharps. This will later also get patched to the SPIRV texture - // binding index. - const IR::Value handle = - ir.CompositeConstruct(ir.GetScalarReg(tsharp_reg), ir.GetScalarReg(sampler_reg)); - - // Load first address components as denoted in 8.2.4 VGPR Usage Sea Islands Series Instruction - // Set Architecture - const IR::U32 offset = - flags.test(MimgModifier::Offset) ? ir.GetVectorReg(addr_reg++) : IR::U32{}; - const IR::F32 bias = - flags.test(MimgModifier::LodBias) ? ir.GetVectorReg(addr_reg++) : IR::F32{}; - const IR::F32 dref = - flags.test(MimgModifier::Pcf) ? ir.GetVectorReg(addr_reg++) : IR::F32{}; - const IR::Value derivatives = [&] -> IR::Value { - if (!flags.test(MimgModifier::Derivative)) { - return {}; - } - addr_reg = addr_reg + 4; - return ir.CompositeConstruct( - ir.GetVectorReg(addr_reg - 4), ir.GetVectorReg(addr_reg - 3), - ir.GetVectorReg(addr_reg - 2), ir.GetVectorReg(addr_reg - 1)); - }(); - - // Now we can load body components as noted in Table 8.9 Image Opcodes with Sampler - // Since these are at most 4 dwords, we load them into a single uvec4 and place them - // in coords field of the instruction. Then the resource tracking pass will patch the - // IR instruction to fill in lod_clamp field. - const IR::Value body = ir.CompositeConstruct( - ir.GetVectorReg(addr_reg), ir.GetVectorReg(addr_reg + 1), - ir.GetVectorReg(addr_reg + 2), ir.GetVectorReg(addr_reg + 3)); - - // Derivatives are tricky because their number depends on the texture type which is located in - // T#. We don't have access to T# though until resource tracking pass. For now assume if - // derivatives are present, that a 2D image is bound. - const bool has_derivatives = flags.test(MimgModifier::Derivative); - const bool explicit_lod = flags.any(MimgModifier::Level0, MimgModifier::Lod); - - IR::TextureInstInfo info{}; - info.is_depth.Assign(flags.test(MimgModifier::Pcf)); - info.has_bias.Assign(flags.test(MimgModifier::LodBias)); - info.has_lod_clamp.Assign(flags.test(MimgModifier::LodClamp)); - info.force_level0.Assign(flags.test(MimgModifier::Level0)); - info.has_offset.Assign(flags.test(MimgModifier::Offset)); - info.explicit_lod.Assign(explicit_lod); - info.has_derivatives.Assign(has_derivatives); - info.is_array.Assign(mimg.da); - - // Issue IR instruction, leaving unknown fields blank to patch later. - const IR::Value texel = [&]() -> IR::Value { - if (has_derivatives) { - return ir.ImageGradient(handle, body, derivatives, offset, {}, info); - } - if (!flags.test(MimgModifier::Pcf)) { - if (explicit_lod) { - return ir.ImageSampleExplicitLod(handle, body, offset, info); - } else { - return ir.ImageSampleImplicitLod(handle, body, bias, offset, info); - } - } - if (explicit_lod) { - return ir.ImageSampleDrefExplicitLod(handle, body, dref, offset, info); - } - return ir.ImageSampleDrefImplicitLod(handle, body, dref, bias, offset, info); - }(); - + const IR::Value texel = EmitImageSample(ir, inst, tsharp_reg, sampler_reg, addr_reg, false); for (u32 i = 0; i < 4; i++) { if (((mimg.dmask >> i) & 1) == 0) { continue; @@ -609,60 +614,13 @@ void Translator::IMAGE_GATHER(const GcnInst& inst) { const IR::ScalarReg sampler_reg{inst.src[3].code * 4}; const auto flags = MimgModifierFlags(mimg.mod); - // Load first dword of T# and S#. We will use them as the handle that will guide resource - // tracking pass where to read the sharps. This will later also get patched to the SPIRV texture - // binding index. - const IR::Value handle = - ir.CompositeConstruct(ir.GetScalarReg(tsharp_reg), ir.GetScalarReg(sampler_reg)); - - // Load first address components as denoted in 8.2.4 VGPR Usage Sea Islands Series Instruction - // Set Architecture - const IR::Value offset = - flags.test(MimgModifier::Offset) ? ir.GetVectorReg(addr_reg++) : IR::Value{}; - const IR::F32 bias = - flags.test(MimgModifier::LodBias) ? ir.GetVectorReg(addr_reg++) : IR::F32{}; - const IR::F32 dref = - flags.test(MimgModifier::Pcf) ? ir.GetVectorReg(addr_reg++) : IR::F32{}; - - // Derivatives are tricky because their number depends on the texture type which is located in - // T#. We don't have access to T# though until resource tracking pass. For now assume no - // derivatives are present, otherwise we don't know where coordinates are placed in the address - // stream. - ASSERT_MSG(!flags.test(MimgModifier::Derivative), "Derivative image instruction"); - - // Now we can load body components as noted in Table 8.9 Image Opcodes with Sampler - // Since these are at most 4 dwords, we load them into a single uvec4 and place them - // in coords field of the instruction. Then the resource tracking pass will patch the - // IR instruction to fill in lod_clamp field. - const IR::Value body = ir.CompositeConstruct( - ir.GetVectorReg(addr_reg), ir.GetVectorReg(addr_reg + 1), - ir.GetVectorReg(addr_reg + 2), ir.GetVectorReg(addr_reg + 3)); - - const bool explicit_lod = flags.any(MimgModifier::Level0, MimgModifier::Lod); - - IR::TextureInstInfo info{}; - info.is_depth.Assign(flags.test(MimgModifier::Pcf)); - info.has_bias.Assign(flags.test(MimgModifier::LodBias)); - info.has_lod_clamp.Assign(flags.test(MimgModifier::LodClamp)); - info.force_level0.Assign(flags.test(MimgModifier::Level0)); - info.has_offset.Assign(flags.test(MimgModifier::Offset)); - // info.explicit_lod.Assign(explicit_lod); - info.gather_comp.Assign(std::bit_width(mimg.dmask) - 1); - info.is_array.Assign(mimg.da); - - // Issue IR instruction, leaving unknown fields blank to patch later. - const IR::Value texel = [&]() -> IR::Value { - const IR::F32 lod = flags.test(MimgModifier::Level0) ? ir.Imm32(0.f) : IR::F32{}; - if (!flags.test(MimgModifier::Pcf)) { - return ir.ImageGather(handle, body, offset, info); - } - ASSERT(mimg.dmask & 1); // should be always 1st (R) component - return ir.ImageGatherDref(handle, body, offset, dref, info); - }(); - // For gather4 instructions dmask selects which component to read and must have // only one bit set to 1 ASSERT_MSG(std::popcount(mimg.dmask) == 1, "Unexpected bits in gather dmask"); + // should be always 1st (R) component for depth + ASSERT(!flags.test(MimgModifier::Pcf) || mimg.dmask & 1); + + const IR::Value texel = EmitImageSample(ir, inst, tsharp_reg, sampler_reg, addr_reg, true); for (u32 i = 0; i < 4; i++) { const IR::F32 value = IR::F32{ir.CompositeExtract(texel, i)}; ir.SetVectorReg(dest_reg++, value); diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index 78a6805fd..e727c8a08 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -3,12 +3,12 @@ #pragma once #include -#include #include #include #include "common/assert.h" #include "common/types.h" #include "shader_recompiler/backend/bindings.h" +#include "shader_recompiler/frontend/copy_shader.h" #include "shader_recompiler/ir/attribute.h" #include "shader_recompiler/ir/reg.h" #include "shader_recompiler/ir/type.h" @@ -170,6 +170,8 @@ struct Info { }; UserDataMask ud_mask{}; + CopyShaderData gs_copy_data; + s8 vertex_offset_sgpr = -1; s8 instance_offset_sgpr = -1; diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 4f5eb5c33..cfd044f9e 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -130,19 +130,23 @@ void IREmitter::DeviceMemoryBarrier() { } U32 IREmitter::GetUserData(IR::ScalarReg reg) { + ASSERT(static_cast(reg) < IR::NumScalarRegs); return Inst(Opcode::GetUserData, reg); } U1 IREmitter::GetThreadBitScalarReg(IR::ScalarReg reg) { + ASSERT(static_cast(reg) < IR::NumScalarRegs); return Inst(Opcode::GetThreadBitScalarReg, reg); } void IREmitter::SetThreadBitScalarReg(IR::ScalarReg reg, const U1& value) { + ASSERT(static_cast(reg) < IR::NumScalarRegs); Inst(Opcode::SetThreadBitScalarReg, reg, value); } template <> U32 IREmitter::GetScalarReg(IR::ScalarReg reg) { + ASSERT(static_cast(reg) < IR::NumScalarRegs); return Inst(Opcode::GetScalarRegister, reg); } @@ -153,6 +157,7 @@ F32 IREmitter::GetScalarReg(IR::ScalarReg reg) { template <> U32 IREmitter::GetVectorReg(IR::VectorReg reg) { + ASSERT(static_cast(reg) < IR::NumVectorRegs); return Inst(Opcode::GetVectorRegister, reg); } @@ -162,11 +167,13 @@ F32 IREmitter::GetVectorReg(IR::VectorReg reg) { } void IREmitter::SetScalarReg(IR::ScalarReg reg, const U32F32& value) { + ASSERT(static_cast(reg) < IR::NumScalarRegs); const U32 value_typed = value.Type() == Type::F32 ? BitCast(F32{value}) : U32{value}; Inst(Opcode::SetScalarRegister, reg, value_typed); } void IREmitter::SetVectorReg(IR::VectorReg reg, const U32F32& value) { + ASSERT(static_cast(reg) < IR::NumVectorRegs); const U32 value_typed = value.Type() == Type::F32 ? BitCast(F32{value}) : U32{value}; Inst(Opcode::SetVectorRegister, reg, value_typed); } @@ -1492,27 +1499,34 @@ Value IREmitter::ImageAtomicExchange(const Value& handle, const Value& coords, c return Inst(Opcode::ImageAtomicExchange32, Flags{info}, handle, coords, value); } -Value IREmitter::ImageSampleImplicitLod(const Value& handle, const Value& body, const F32& bias, - const U32& offset, TextureInstInfo info) { - return Inst(Opcode::ImageSampleImplicitLod, Flags{info}, handle, body, bias, offset); +Value IREmitter::ImageSampleRaw(const Value& handle, const Value& address1, const Value& address2, + const Value& address3, const Value& address4, + TextureInstInfo info) { + return Inst(Opcode::ImageSampleRaw, Flags{info}, handle, address1, address2, address3, + address4); } -Value IREmitter::ImageSampleExplicitLod(const Value& handle, const Value& body, const U32& offset, - TextureInstInfo info) { - return Inst(Opcode::ImageSampleExplicitLod, Flags{info}, handle, body, IR::F32{}, offset); +Value IREmitter::ImageSampleImplicitLod(const Value& handle, const Value& coords, const F32& bias, + const Value& offset, TextureInstInfo info) { + return Inst(Opcode::ImageSampleImplicitLod, Flags{info}, handle, coords, bias, offset); } -F32 IREmitter::ImageSampleDrefImplicitLod(const Value& handle, const Value& body, const F32& dref, - const F32& bias, const U32& offset, - TextureInstInfo info) { - return Inst(Opcode::ImageSampleDrefImplicitLod, Flags{info}, handle, body, dref, bias, - offset); +Value IREmitter::ImageSampleExplicitLod(const Value& handle, const Value& coords, const F32& lod, + const Value& offset, TextureInstInfo info) { + return Inst(Opcode::ImageSampleExplicitLod, Flags{info}, handle, coords, lod, offset); } -F32 IREmitter::ImageSampleDrefExplicitLod(const Value& handle, const Value& body, const F32& dref, - const U32& offset, TextureInstInfo info) { - return Inst(Opcode::ImageSampleDrefExplicitLod, Flags{info}, handle, body, dref, IR::F32{}, - offset); +Value IREmitter::ImageSampleDrefImplicitLod(const Value& handle, const Value& coords, + const F32& dref, const F32& bias, const Value& offset, + TextureInstInfo info) { + return Inst(Opcode::ImageSampleDrefImplicitLod, Flags{info}, handle, coords, dref, bias, + offset); +} + +Value IREmitter::ImageSampleDrefExplicitLod(const Value& handle, const Value& coords, + const F32& dref, const F32& lod, const Value& offset, + TextureInstInfo info) { + return Inst(Opcode::ImageSampleDrefExplicitLod, Flags{info}, handle, coords, dref, lod, offset); } Value IREmitter::ImageGather(const Value& handle, const Value& coords, const Value& offset, @@ -1544,9 +1558,11 @@ Value IREmitter::ImageQueryLod(const Value& handle, const Value& coords, Texture return Inst(Opcode::ImageQueryLod, Flags{info}, handle, coords); } -Value IREmitter::ImageGradient(const Value& handle, const Value& coords, const Value& derivatives, +Value IREmitter::ImageGradient(const Value& handle, const Value& coords, + const Value& derivatives_dx, const Value& derivatives_dy, const Value& offset, const F32& lod_clamp, TextureInstInfo info) { - return Inst(Opcode::ImageGradient, Flags{info}, handle, coords, derivatives, offset, lod_clamp); + return Inst(Opcode::ImageGradient, Flags{info}, handle, coords, derivatives_dx, derivatives_dy, + offset, lod_clamp); } Value IREmitter::ImageRead(const Value& handle, const Value& coords, TextureInstInfo info) { diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index 2ebac037e..b3f513085 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -277,20 +277,25 @@ public: [[nodiscard]] Value ImageAtomicExchange(const Value& handle, const Value& coords, const Value& value, TextureInstInfo info); + [[nodiscard]] Value ImageSampleRaw(const Value& handle, const Value& address1, + const Value& address2, const Value& address3, + const Value& address4, TextureInstInfo info); + [[nodiscard]] Value ImageSampleImplicitLod(const Value& handle, const Value& body, - const F32& bias, const U32& offset, + const F32& bias, const Value& offset, TextureInstInfo info); [[nodiscard]] Value ImageSampleExplicitLod(const Value& handle, const Value& body, - const U32& offset, TextureInstInfo info); + const F32& lod, const Value& offset, + TextureInstInfo info); - [[nodiscard]] F32 ImageSampleDrefImplicitLod(const Value& handle, const Value& body, - const F32& dref, const F32& bias, - const U32& offset, TextureInstInfo info); + [[nodiscard]] Value ImageSampleDrefImplicitLod(const Value& handle, const Value& body, + const F32& dref, const F32& bias, + const Value& offset, TextureInstInfo info); - [[nodiscard]] F32 ImageSampleDrefExplicitLod(const Value& handle, const Value& body, - const F32& dref, const U32& offset, - TextureInstInfo info); + [[nodiscard]] Value ImageSampleDrefExplicitLod(const Value& handle, const Value& body, + const F32& dref, const F32& lod, + const Value& offset, TextureInstInfo info); [[nodiscard]] Value ImageQueryDimension(const Value& handle, const U32& lod, const U1& skip_mips); @@ -306,8 +311,9 @@ public: [[nodiscard]] Value ImageFetch(const Value& handle, const Value& coords, const Value& offset, const U32& lod, const U32& multisampling, TextureInstInfo info); [[nodiscard]] Value ImageGradient(const Value& handle, const Value& coords, - const Value& derivatives, const Value& offset, - const F32& lod_clamp, TextureInstInfo info); + const Value& derivatives_dx, const Value& derivatives_dy, + const Value& offset, const F32& lod_clamp, + TextureInstInfo info); [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info); void ImageWrite(const Value& handle, const Value& coords, const Value& color, TextureInstInfo info); diff --git a/src/shader_recompiler/ir/opcodes.h b/src/shader_recompiler/ir/opcodes.h index 2cea70090..200d7f421 100644 --- a/src/shader_recompiler/ir/opcodes.h +++ b/src/shader_recompiler/ir/opcodes.h @@ -21,7 +21,7 @@ namespace Detail { struct OpcodeMeta { std::string_view name; Type type; - std::array arg_types; + std::array arg_types; }; // using enum Type; diff --git a/src/shader_recompiler/ir/opcodes.inc b/src/shader_recompiler/ir/opcodes.inc index 41e94ab13..51e10fb38 100644 --- a/src/shader_recompiler/ir/opcodes.inc +++ b/src/shader_recompiler/ir/opcodes.inc @@ -317,16 +317,17 @@ OPCODE(ConvertU16U32, U16, U32, OPCODE(ConvertU32U16, U32, U16, ) // Image operations -OPCODE(ImageSampleImplicitLod, F32x4, Opaque, Opaque, F32, Opaque, ) -OPCODE(ImageSampleExplicitLod, F32x4, Opaque, Opaque, U32, Opaque, ) -OPCODE(ImageSampleDrefImplicitLod, F32, Opaque, Opaque, Opaque, F32, Opaque, ) -OPCODE(ImageSampleDrefExplicitLod, F32, Opaque, Opaque, Opaque, U32, Opaque, ) +OPCODE(ImageSampleRaw, F32x4, Opaque, F32x4, F32x4, F32x4, F32, ) +OPCODE(ImageSampleImplicitLod, F32x4, Opaque, F32x4, F32, Opaque, ) +OPCODE(ImageSampleExplicitLod, F32x4, Opaque, Opaque, F32, Opaque, ) +OPCODE(ImageSampleDrefImplicitLod, F32x4, Opaque, Opaque, F32, F32, Opaque, ) +OPCODE(ImageSampleDrefExplicitLod, F32x4, Opaque, Opaque, F32, F32, Opaque, ) OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, ) OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, F32, ) OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, ) OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, U1, ) OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, ) -OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, ) +OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, F32, ) OPCODE(ImageRead, U32x4, Opaque, Opaque, ) OPCODE(ImageWrite, Void, Opaque, Opaque, U32x4, ) diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index db0d75f0c..0d91badda 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -132,38 +132,16 @@ bool IsImageStorageInstruction(const IR::Inst& inst) { bool IsImageInstruction(const IR::Inst& inst) { switch (inst.GetOpcode()) { - case IR::Opcode::ImageSampleExplicitLod: - case IR::Opcode::ImageSampleImplicitLod: - case IR::Opcode::ImageSampleDrefExplicitLod: - case IR::Opcode::ImageSampleDrefImplicitLod: case IR::Opcode::ImageFetch: - case IR::Opcode::ImageGather: - case IR::Opcode::ImageGatherDref: case IR::Opcode::ImageQueryDimensions: case IR::Opcode::ImageQueryLod: - case IR::Opcode::ImageGradient: + case IR::Opcode::ImageSampleRaw: return true; default: return IsImageStorageInstruction(inst); } } -u32 ImageOffsetArgumentPosition(const IR::Inst& inst) { - switch (inst.GetOpcode()) { - case IR::Opcode::ImageGather: - case IR::Opcode::ImageGatherDref: - return 2; - case IR::Opcode::ImageSampleExplicitLod: - case IR::Opcode::ImageSampleImplicitLod: - return 3; - case IR::Opcode::ImageSampleDrefExplicitLod: - case IR::Opcode::ImageSampleDrefImplicitLod: - return 4; - default: - UNREACHABLE(); - } -} - class Descriptors { public: explicit Descriptors(Info& info_) @@ -467,6 +445,191 @@ IR::Value PatchCubeCoord(IR::IREmitter& ir, const IR::Value& s, const IR::Value& } } +void PatchImageSampleInstruction(IR::Block& block, IR::Inst& inst, Info& info, + Descriptors& descriptors, const IR::Inst* producer, + const u32 image_binding, const AmdGpu::Image& image) { + // Read sampler sharp. This doesn't exist for IMAGE_LOAD/IMAGE_STORE instructions + const u32 sampler_binding = [&] { + ASSERT(producer->GetOpcode() == IR::Opcode::CompositeConstructU32x2); + const IR::Value& handle = producer->Arg(1); + // Inline sampler resource. + if (handle.IsImmediate()) { + LOG_WARNING(Render_Vulkan, "Inline sampler detected"); + return descriptors.Add(SamplerResource{ + .sgpr_base = std::numeric_limits::max(), + .dword_offset = 0, + .inline_sampler = AmdGpu::Sampler{.raw0 = handle.U32()}, + }); + } + // Normal sampler resource. + const auto ssharp_handle = handle.InstRecursive(); + const auto& [ssharp_ud, disable_aniso] = TryDisableAnisoLod0(ssharp_handle); + const auto ssharp = TrackSharp(ssharp_ud); + return descriptors.Add(SamplerResource{ + .sgpr_base = ssharp.sgpr_base, + .dword_offset = ssharp.dword_offset, + .associated_image = image_binding, + .disable_aniso = disable_aniso, + }); + }(); + + IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; + + const auto inst_info = inst.Flags(); + const IR::U32 handle = ir.Imm32(image_binding | sampler_binding << 16); + + IR::Inst* body1 = inst.Arg(1).InstRecursive(); + IR::Inst* body2 = inst.Arg(2).InstRecursive(); + IR::Inst* body3 = inst.Arg(3).InstRecursive(); + IR::F32 body4 = IR::F32{inst.Arg(4)}; + const auto get_addr_reg = [&](u32 index) -> IR::F32 { + if (index <= 3) { + return IR::F32{body1->Arg(index)}; + } + if (index >= 4 && index <= 7) { + return IR::F32{body2->Arg(index - 4)}; + } + if (index >= 8 && index <= 11) { + return IR::F32{body3->Arg(index - 8)}; + } + if (index == 12) { + return body4; + } + UNREACHABLE(); + }; + u32 addr_reg = 0; + + // Load first address components as denoted in 8.2.4 VGPR Usage Sea Islands Series Instruction + // Set Architecture + const IR::Value offset = [&] -> IR::Value { + if (!inst_info.has_offset) { + return IR::U32{}; + } + + // The offsets are six-bit signed integers: X=[5:0], Y=[13:8], and Z=[21:16]. + IR::Value arg = get_addr_reg(addr_reg++); + if (const IR::Inst* offset_inst = arg.TryInstRecursive()) { + ASSERT(offset_inst->GetOpcode() == IR::Opcode::BitCastF32U32); + arg = offset_inst->Arg(0); + } + + const auto read = [&](u32 off) -> IR::U32 { + if (arg.IsImmediate()) { + const u32 imm = + arg.Type() == IR::Type::F32 ? std::bit_cast(arg.F32()) : arg.U32(); + const u16 comp = (imm >> off) & 0x3F; + return ir.Imm32(s32(comp << 26) >> 26); + } + return ir.BitFieldExtract(IR::U32{arg}, ir.Imm32(off), ir.Imm32(6), true); + }; + + switch (image.GetType()) { + case AmdGpu::ImageType::Color1D: + case AmdGpu::ImageType::Color1DArray: + return read(0); + case AmdGpu::ImageType::Color2D: + case AmdGpu::ImageType::Color2DArray: + case AmdGpu::ImageType::Color2DMsaa: + return ir.CompositeConstruct(read(0), read(8)); + case AmdGpu::ImageType::Color3D: + case AmdGpu::ImageType::Cube: + return ir.CompositeConstruct(read(0), read(8), read(16)); + default: + UNREACHABLE(); + } + }(); + const IR::F32 bias = inst_info.has_bias ? get_addr_reg(addr_reg++) : IR::F32{}; + const IR::F32 dref = inst_info.is_depth ? get_addr_reg(addr_reg++) : IR::F32{}; + const auto [derivatives_dx, derivatives_dy] = [&] -> std::pair { + if (!inst_info.has_derivatives) { + return {}; + } + switch (image.GetType()) { + case AmdGpu::ImageType::Color1D: + case AmdGpu::ImageType::Color1DArray: + // du/dx, du/dy + addr_reg = addr_reg + 2; + return {get_addr_reg(addr_reg - 2), get_addr_reg(addr_reg - 1)}; + case AmdGpu::ImageType::Color2D: + case AmdGpu::ImageType::Color2DArray: + case AmdGpu::ImageType::Color2DMsaa: + // (du/dx, dv/dx), (du/dy, dv/dy) + addr_reg = addr_reg + 4; + return {ir.CompositeConstruct(get_addr_reg(addr_reg - 4), get_addr_reg(addr_reg - 3)), + ir.CompositeConstruct(get_addr_reg(addr_reg - 2), get_addr_reg(addr_reg - 1))}; + case AmdGpu::ImageType::Color3D: + case AmdGpu::ImageType::Cube: + // (du/dx, dv/dx, dw/dx), (du/dy, dv/dy, dw/dy) + addr_reg = addr_reg + 6; + return {ir.CompositeConstruct(get_addr_reg(addr_reg - 6), get_addr_reg(addr_reg - 5), + get_addr_reg(addr_reg - 4)), + ir.CompositeConstruct(get_addr_reg(addr_reg - 3), get_addr_reg(addr_reg - 2), + get_addr_reg(addr_reg - 1))}; + default: + UNREACHABLE(); + } + }(); + + // Now we can load body components as noted in Table 8.9 Image Opcodes with Sampler + const IR::Value coords = [&] -> IR::Value { + switch (image.GetType()) { + case AmdGpu::ImageType::Color1D: // x + addr_reg = addr_reg + 1; + return get_addr_reg(addr_reg - 1); + case AmdGpu::ImageType::Color1DArray: // x, slice + [[fallthrough]]; + case AmdGpu::ImageType::Color2D: // x, y + addr_reg = addr_reg + 2; + return ir.CompositeConstruct(get_addr_reg(addr_reg - 2), get_addr_reg(addr_reg - 1)); + case AmdGpu::ImageType::Color2DArray: // x, y, slice + [[fallthrough]]; + case AmdGpu::ImageType::Color2DMsaa: // x, y, frag + [[fallthrough]]; + case AmdGpu::ImageType::Color3D: // x, y, z + addr_reg = addr_reg + 3; + return ir.CompositeConstruct(get_addr_reg(addr_reg - 3), get_addr_reg(addr_reg - 2), + get_addr_reg(addr_reg - 1)); + case AmdGpu::ImageType::Cube: // x, y, face + addr_reg = addr_reg + 3; + return PatchCubeCoord(ir, get_addr_reg(addr_reg - 3), get_addr_reg(addr_reg - 2), + get_addr_reg(addr_reg - 1), false, inst_info.is_array); + default: + UNREACHABLE(); + } + }(); + + ASSERT(!inst_info.has_lod || !inst_info.has_lod_clamp); + const bool explicit_lod = inst_info.has_lod || inst_info.force_level0; + const IR::F32 lod = inst_info.has_lod ? get_addr_reg(addr_reg++) + : inst_info.force_level0 ? ir.Imm32(0.0f) + : IR::F32{}; + const IR::F32 lod_clamp = inst_info.has_lod_clamp ? get_addr_reg(addr_reg++) : IR::F32{}; + + const auto new_inst = [&] -> IR::Value { + if (inst_info.is_gather) { + if (inst_info.is_depth) { + return ir.ImageGatherDref(handle, coords, offset, dref, inst_info); + } + return ir.ImageGather(handle, coords, offset, inst_info); + } + if (inst_info.has_derivatives) { + return ir.ImageGradient(handle, coords, derivatives_dx, derivatives_dy, offset, + lod_clamp, inst_info); + } + if (inst_info.is_depth) { + if (explicit_lod) { + return ir.ImageSampleDrefExplicitLod(handle, coords, dref, lod, offset, inst_info); + } + return ir.ImageSampleDrefImplicitLod(handle, coords, dref, bias, offset, inst_info); + } + if (explicit_lod) { + return ir.ImageSampleExplicitLod(handle, coords, lod, offset, inst_info); + } + return ir.ImageSampleImplicitLod(handle, coords, bias, offset, inst_info); + }(); + inst.ReplaceUsesWith(new_inst); +} + void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& descriptors) { const auto pred = [](const IR::Inst* inst) -> std::optional { const auto opcode = inst->GetOpcode(); @@ -498,40 +661,18 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip .sgpr_base = tsharp.sgpr_base, .dword_offset = tsharp.dword_offset, .type = type, - .nfmt = static_cast(image.GetNumberFmt()), + .nfmt = image.GetNumberFmt(), .is_storage = is_storage, .is_depth = bool(inst_info.is_depth), .is_atomic = IsImageAtomicInstruction(inst), .is_array = bool(inst_info.is_array), }); - // Read sampler sharp. This doesn't exist for IMAGE_LOAD/IMAGE_STORE instructions - const u32 sampler_binding = [&] { - if (!has_sampler) { - return 0U; - } - const IR::Value& handle = producer->Arg(1); - // Inline sampler resource. - if (handle.IsImmediate()) { - LOG_WARNING(Render_Vulkan, "Inline sampler detected"); - return descriptors.Add(SamplerResource{ - .sgpr_base = std::numeric_limits::max(), - .dword_offset = 0, - .inline_sampler = AmdGpu::Sampler{.raw0 = handle.U32()}, - }); - } - // Normal sampler resource. - const auto ssharp_handle = handle.InstRecursive(); - const auto& [ssharp_ud, disable_aniso] = TryDisableAnisoLod0(ssharp_handle); - const auto ssharp = TrackSharp(ssharp_ud); - return descriptors.Add(SamplerResource{ - .sgpr_base = ssharp.sgpr_base, - .dword_offset = ssharp.dword_offset, - .associated_image = image_binding, - .disable_aniso = disable_aniso, - }); - }(); - image_binding |= (sampler_binding << 16); + // Sample instructions must be resolved into a new instruction using address register data. + if (inst.GetOpcode() == IR::Opcode::ImageSampleRaw) { + PatchImageSampleInstruction(block, inst, info, descriptors, producer, image_binding, image); + return; + } // Patch image handle IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; @@ -568,62 +709,9 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip }(); inst.SetArg(1, coords); - if (inst_info.has_offset) { - // The offsets are six-bit signed integers: X=[5:0], Y=[13:8], and Z=[21:16]. - const u32 arg_pos = ImageOffsetArgumentPosition(inst); - const IR::Value arg = inst.Arg(arg_pos); - ASSERT_MSG(arg.Type() == IR::Type::U32, "Unexpected offset type"); - - const auto read = [&](u32 offset) -> IR::U32 { - if (arg.IsImmediate()) { - const u16 comp = (arg.U32() >> offset) & 0x3F; - return ir.Imm32(s32(comp << 26) >> 26); - } - return ir.BitFieldExtract(IR::U32{arg}, ir.Imm32(offset), ir.Imm32(6), true); - }; - - switch (image.GetType()) { - case AmdGpu::ImageType::Color1D: - case AmdGpu::ImageType::Color1DArray: - inst.SetArg(arg_pos, read(0)); - break; - case AmdGpu::ImageType::Color2D: - case AmdGpu::ImageType::Color2DArray: - inst.SetArg(arg_pos, ir.CompositeConstruct(read(0), read(8))); - break; - case AmdGpu::ImageType::Color3D: - inst.SetArg(arg_pos, ir.CompositeConstruct(read(0), read(8), read(16))); - break; - default: - UNREACHABLE(); - } - } - if (inst_info.has_derivatives) { - ASSERT_MSG(image.GetType() == AmdGpu::ImageType::Color2D || - image.GetType() == AmdGpu::ImageType::Color2DArray, - "User derivatives only supported for 2D images"); - } - if (inst_info.has_lod_clamp) { - const u32 arg_pos = [&]() -> u32 { - switch (inst.GetOpcode()) { - case IR::Opcode::ImageSampleImplicitLod: - return 2; - case IR::Opcode::ImageSampleDrefImplicitLod: - return 3; - default: - break; - } - return inst_info.is_depth ? 5 : 4; - }(); - inst.SetArg(arg_pos, arg); - } - if (inst_info.explicit_lod) { - ASSERT(inst.GetOpcode() == IR::Opcode::ImageFetch || - inst.GetOpcode() == IR::Opcode::ImageSampleExplicitLod || - inst.GetOpcode() == IR::Opcode::ImageSampleDrefExplicitLod); - const u32 pos = inst.GetOpcode() == IR::Opcode::ImageSampleExplicitLod ? 2 : 3; - const IR::Value value = inst_info.force_level0 ? ir.Imm32(0.f) : arg; - inst.SetArg(pos, value); + if (inst_info.has_lod) { + ASSERT(inst.GetOpcode() == IR::Opcode::ImageFetch); + inst.SetArg(3, arg); } } diff --git a/src/shader_recompiler/ir/passes/ring_access_elimination.cpp b/src/shader_recompiler/ir/passes/ring_access_elimination.cpp index 857921b1f..eb1be2967 100644 --- a/src/shader_recompiler/ir/passes/ring_access_elimination.cpp +++ b/src/shader_recompiler/ir/passes/ring_access_elimination.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "shader_recompiler/frontend/translate/translate.h" +#include "shader_recompiler/ir/ir_emitter.h" #include "shader_recompiler/ir/opcodes.h" #include "shader_recompiler/ir/program.h" #include "shader_recompiler/ir/reg.h" @@ -11,6 +11,8 @@ namespace Shader::Optimization { void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtime_info, Stage stage) { + auto& info = program.info; + const auto& ForEachInstruction = [&](auto func) { for (IR::Block* block : program.blocks) { for (IR::Inst& inst : block->Instructions()) { @@ -52,6 +54,9 @@ void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtim break; } case Stage::Geometry: { + const auto& gs_info = runtime_info.gs_info; + info.gs_copy_data = Shader::ParseCopyShader(gs_info.vs_copy); + ForEachInstruction([&](IR::IREmitter& ir, IR::Inst& inst) { const auto opcode = inst.GetOpcode(); switch (opcode) { @@ -81,12 +86,12 @@ void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtim const auto offset = inst.Flags().inst_offset.Value(); const auto data = ir.BitCast(IR::U32{inst.Arg(2)}); - const auto comp_ofs = runtime_info.gs_info.output_vertices * 4u; - const auto output_size = comp_ofs * runtime_info.gs_info.out_vertex_data_size; + const auto comp_ofs = gs_info.output_vertices * 4u; + const auto output_size = comp_ofs * gs_info.out_vertex_data_size; const auto vc_read_ofs = (((offset / comp_ofs) * comp_ofs) % output_size) * 16u; - const auto& it = runtime_info.gs_info.copy_data.attr_map.find(vc_read_ofs); - ASSERT(it != runtime_info.gs_info.copy_data.attr_map.cend()); + const auto& it = info.gs_copy_data.attr_map.find(vc_read_ofs); + ASSERT(it != info.gs_copy_data.attr_map.cend()); const auto& [attr, comp] = it->second; inst.ReplaceOpcode(IR::Opcode::SetAttribute); diff --git a/src/shader_recompiler/ir/reg.h b/src/shader_recompiler/ir/reg.h index 9ec77e5f0..d7c0b1db5 100644 --- a/src/shader_recompiler/ir/reg.h +++ b/src/shader_recompiler/ir/reg.h @@ -10,20 +10,6 @@ namespace Shader::IR { -enum class FpRoundMode : u32 { - NearestEven = 0, - PlusInf = 1, - MinInf = 2, - ToZero = 3, -}; - -enum class FpDenormMode : u32 { - InOutFlush = 0, - InAllowOutFlush = 1, - InFlushOutAllow = 2, - InOutAllow = 3, -}; - enum class FloatClassFunc : u32 { SignalingNan = 1 << 0, QuietNan = 1 << 1, @@ -41,24 +27,18 @@ enum class FloatClassFunc : u32 { }; DECLARE_ENUM_FLAG_OPERATORS(FloatClassFunc) -union Mode { - BitField<0, 4, FpRoundMode> fp_round; - BitField<4, 2, FpDenormMode> fp_denorm_single; - BitField<6, 2, FpDenormMode> fp_denorm_double; - BitField<8, 1, u32> dx10_clamp; -}; - union TextureInstInfo { u32 raw; BitField<0, 1, u32> is_depth; BitField<1, 1, u32> has_bias; BitField<2, 1, u32> has_lod_clamp; BitField<3, 1, u32> force_level0; - BitField<4, 1, u32> explicit_lod; + BitField<4, 1, u32> has_lod; BitField<5, 1, u32> has_offset; BitField<6, 2, u32> gather_comp; BitField<8, 1, u32> has_derivatives; BitField<9, 1, u32> is_array; + BitField<10, 1, u32> is_gather; }; union BufferInstInfo { diff --git a/src/shader_recompiler/ir/value.h b/src/shader_recompiler/ir/value.h index 060b9d2bb..a282b9168 100644 --- a/src/shader_recompiler/ir/value.h +++ b/src/shader_recompiler/ir/value.h @@ -209,7 +209,7 @@ private: union { NonTriviallyDummy dummy{}; boost::container::small_vector, 2> phi_args; - std::array args; + std::array args; }; }; static_assert(sizeof(Inst) <= 128, "Inst size unintentionally increased"); diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index badd54554..bbda731e0 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -19,13 +19,8 @@ struct Profile { bool support_float_controls{}; bool support_separate_denorm_behavior{}; bool support_separate_rounding_mode{}; - bool support_fp16_denorm_preserve{}; bool support_fp32_denorm_preserve{}; - bool support_fp16_denorm_flush{}; bool support_fp32_denorm_flush{}; - bool support_fp16_signed_zero_nan_preserve{}; - bool support_fp32_signed_zero_nan_preserve{}; - bool support_fp64_signed_zero_nan_preserve{}; bool support_explicit_workgroup_layout{}; bool has_broken_spirv_clamp{}; bool lower_left_origin_mode{}; diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 8c0838c96..03936f3a8 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -4,11 +4,9 @@ #pragma once #include +#include #include - -#include "common/assert.h" #include "common/types.h" -#include "frontend/copy_shader.h" #include "video_core/amdgpu/types.h" namespace Shader { @@ -62,7 +60,8 @@ enum class VsOutput : u8 { using VsOutputMap = std::array; struct VertexRuntimeInfo { - boost::container::static_vector outputs; + u32 num_outputs; + std::array outputs; bool emulate_depth_negative_one_to_one{}; bool operator==(const VertexRuntimeInfo& other) const noexcept { @@ -79,13 +78,13 @@ struct GeometryRuntimeInfo { u32 out_vertex_data_size{}; AmdGpu::PrimitiveType in_primitive; GsOutputPrimTypes out_primitive; - CopyShaderData copy_data; + std::span vs_copy; + u64 vs_copy_hash; bool operator==(const GeometryRuntimeInfo& other) const noexcept { return num_invocations && other.num_invocations && output_vertices == other.output_vertices && in_primitive == other.in_primitive && - std::ranges::equal(out_primitive, other.out_primitive) && - std::ranges::equal(copy_data.attr_map, other.copy_data.attr_map); + std::ranges::equal(out_primitive, other.out_primitive); } }; @@ -106,7 +105,8 @@ struct FragmentRuntimeInfo { auto operator<=>(const PsInput&) const noexcept = default; }; - boost::container::static_vector inputs; + u32 num_inputs; + std::array inputs; struct PsColorBuffer { AmdGpu::NumberFormat num_format; MrtSwizzle mrt_swizzle; @@ -117,7 +117,9 @@ struct FragmentRuntimeInfo { bool operator==(const FragmentRuntimeInfo& other) const noexcept { return std::ranges::equal(color_buffers, other.color_buffers) && - std::ranges::equal(inputs, other.inputs); + num_inputs == other.num_inputs && + std::ranges::equal(inputs.begin(), inputs.begin() + num_inputs, other.inputs.begin(), + other.inputs.begin() + num_inputs); } }; @@ -141,13 +143,20 @@ struct RuntimeInfo { u32 num_user_data; u32 num_input_vgprs; u32 num_allocated_vgprs; - ExportRuntimeInfo es_info; - VertexRuntimeInfo vs_info; - GeometryRuntimeInfo gs_info; - FragmentRuntimeInfo fs_info; - ComputeRuntimeInfo cs_info; + AmdGpu::FpDenormMode fp_denorm_mode32; + AmdGpu::FpRoundMode fp_round_mode32; + union { + ExportRuntimeInfo es_info; + VertexRuntimeInfo vs_info; + GeometryRuntimeInfo gs_info; + FragmentRuntimeInfo fs_info; + ComputeRuntimeInfo cs_info; + }; - RuntimeInfo(Stage stage_) : stage{stage_} {} + RuntimeInfo(Stage stage_) { + memset(this, 0, sizeof(*this)); + stage = stage_; + } bool operator==(const RuntimeInfo& other) const noexcept { switch (stage) { diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 3dce871fe..b3b718836 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -226,6 +226,17 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanheader.count.Value() * 2; + const std::string_view label{reinterpret_cast(&nop->data_block[1]), + marker_sz}; + const u32 color = *reinterpret_cast( + reinterpret_cast(&nop->data_block[1]) + marker_sz); + if (rasterizer) { + rasterizer->ScopedMarkerInsertColor(label, color); + } + break; + } case PM4CmdNop::PayloadType::DebugMarkerPop: { if (rasterizer) { rasterizer->ScopeMarkerEnd(); diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index 508420bca..1c994d0a0 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -92,6 +92,12 @@ struct Liverpool { union { BitField<0, 6, u64> num_vgprs; BitField<6, 4, u64> num_sgprs; + BitField<10, 2, u64> priority; + BitField<12, 2, FpRoundMode> fp_round_mode32; + BitField<14, 2, FpRoundMode> fp_round_mode64; + BitField<16, 2, FpDenormMode> fp_denorm_mode32; + BitField<18, 2, FpDenormMode> fp_denorm_mode64; + BitField<12, 8, u64> float_mode; BitField<24, 2, u64> vgpr_comp_cnt; // SPI provided per-thread inputs BitField<33, 5, u64> num_user_regs; } settings; diff --git a/src/video_core/amdgpu/types.h b/src/video_core/amdgpu/types.h index 8cc023a79..6b95ed910 100644 --- a/src/video_core/amdgpu/types.h +++ b/src/video_core/amdgpu/types.h @@ -7,6 +7,20 @@ namespace AmdGpu { +enum class FpRoundMode : u32 { + NearestEven = 0, + PlusInf = 1, + MinInf = 2, + ToZero = 3, +}; + +enum class FpDenormMode : u32 { + InOutFlush = 0, + InAllowOutFlush = 1, + InFlushOutAllow = 2, + InOutAllow = 3, +}; + // See `VGT_PRIMITIVE_TYPE` description in [Radeon Sea Islands 3D/Compute Register Reference Guide] enum class PrimitiveType : u32 { None = 0, @@ -103,4 +117,4 @@ enum class NumberFormat : u32 { Ubscaled = 13, }; -} // namespace AmdGpu \ No newline at end of file +} // namespace AmdGpu diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 97e5185e5..64a483654 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -446,6 +446,8 @@ Frame* RendererVulkan::GetRenderFrame() { // Wait for the presentation to be finished so all frame resources are free while (wait() != vk::Result::eSuccess) { + ASSERT_MSG(result != vk::Result::eErrorDeviceLost, + "Device lost during waiting for a frame"); // Retry if the waiting times out if (result == vk::Result::eTimeout) { continue; diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 7a1d784fa..dda4e0d9f 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -217,9 +217,10 @@ bool Instance::CreateDevice() { const vk::StructureChain properties_chain = physical_device.getProperties2< vk::PhysicalDeviceProperties2, vk::PhysicalDevicePortabilitySubsetPropertiesKHR, vk::PhysicalDeviceExternalMemoryHostPropertiesEXT, vk::PhysicalDeviceVulkan11Properties, - vk::PhysicalDevicePushDescriptorPropertiesKHR>(); + vk::PhysicalDevicePushDescriptorPropertiesKHR, vk::PhysicalDeviceVulkan12Properties>(); subgroup_size = properties_chain.get().subgroupSize; push_descriptor_props = properties_chain.get(); + vk12_props = properties_chain.get(); LOG_INFO(Render_Vulkan, "Physical device subgroup size {}", subgroup_size); features = feature_chain.get().features; @@ -265,7 +266,9 @@ bool Instance::CreateDevice() { // These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2 // with extensions. - tooling_info = add_extension(VK_EXT_TOOLING_INFO_EXTENSION_NAME); + if (Config::vkValidationEnabled() || Config::isRdocEnabled()) { + tooling_info = add_extension(VK_EXT_TOOLING_INFO_EXTENSION_NAME); + } const bool maintenance4 = add_extension(VK_KHR_MAINTENANCE_4_EXTENSION_NAME); add_extension(VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME); add_extension(VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME); diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index d77d0c20f..474b86e9a 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -242,6 +242,11 @@ public: return push_descriptor_props.maxPushDescriptors; } + /// Returns the vulkan 1.2 physical device properties. + const vk::PhysicalDeviceVulkan12Properties& GetVk12Properties() const noexcept { + return vk12_props; + } + /// Returns true if shaders can declare the ClipDistance attribute bool IsShaderClipDistanceSupported() const { return features.shaderClipDistance; @@ -279,6 +284,7 @@ private: vk::UniqueDevice device; vk::PhysicalDeviceProperties properties; vk::PhysicalDevicePushDescriptorPropertiesKHR push_descriptor_props; + vk::PhysicalDeviceVulkan12Properties vk12_props; vk::PhysicalDeviceFeatures features; vk::DriverIdKHR driver_id; vk::UniqueDebugUtilsMessengerEXT debug_callback{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 929fa9cc1..a06d82eb3 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -7,7 +7,6 @@ #include "common/io_file.h" #include "common/path_util.h" #include "shader_recompiler/backend/spirv/emit_spirv.h" -#include "shader_recompiler/frontend/copy_shader.h" #include "shader_recompiler/info.h" #include "shader_recompiler/recompiler.h" #include "shader_recompiler/runtime_info.h" @@ -41,7 +40,7 @@ void GatherVertexOutputs(Shader::VertexRuntimeInfo& info, const auto add_output = [&](VsOutput x, VsOutput y, VsOutput z, VsOutput w) { if (x != VsOutput::None || y != VsOutput::None || z != VsOutput::None || w != VsOutput::None) { - info.outputs.emplace_back(Shader::VsOutputMap{x, y, z, w}); + info.outputs[info.num_outputs++] = Shader::VsOutputMap{x, y, z, w}; } }; // VS_OUT_MISC_VEC @@ -84,18 +83,21 @@ void GatherVertexOutputs(Shader::VertexRuntimeInfo& info, Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) { auto info = Shader::RuntimeInfo{stage}; const auto& regs = liverpool->regs; + const auto BuildCommon = [&](const auto& program) { + info.num_user_data = program.settings.num_user_regs; + info.num_input_vgprs = program.settings.vgpr_comp_cnt; + info.num_allocated_vgprs = program.settings.num_vgprs * 4; + info.fp_denorm_mode32 = program.settings.fp_denorm_mode32; + info.fp_round_mode32 = program.settings.fp_round_mode32; + }; switch (stage) { case Shader::Stage::Export: { - info.num_user_data = regs.es_program.settings.num_user_regs; - info.num_input_vgprs = regs.es_program.settings.vgpr_comp_cnt; - info.num_allocated_vgprs = regs.es_program.settings.num_vgprs * 4; + BuildCommon(regs.es_program); info.es_info.vertex_data_size = regs.vgt_esgs_ring_itemsize; break; } case Shader::Stage::Vertex: { - info.num_user_data = regs.vs_program.settings.num_user_regs; - info.num_input_vgprs = regs.vs_program.settings.vgpr_comp_cnt; - info.num_allocated_vgprs = regs.vs_program.settings.num_vgprs * 4; + BuildCommon(regs.vs_program); GatherVertexOutputs(info.vs_info, regs.vs_output_control); info.vs_info.emulate_depth_negative_one_to_one = !instance.IsDepthClipControlSupported() && @@ -103,39 +105,35 @@ Shader::RuntimeInfo PipelineCache::BuildRuntimeInfo(Shader::Stage stage) { break; } case Shader::Stage::Geometry: { - info.num_user_data = regs.gs_program.settings.num_user_regs; - info.num_input_vgprs = regs.gs_program.settings.vgpr_comp_cnt; - info.num_allocated_vgprs = regs.gs_program.settings.num_vgprs * 4; - info.gs_info.output_vertices = regs.vgt_gs_max_vert_out; - info.gs_info.num_invocations = + BuildCommon(regs.gs_program); + auto& gs_info = info.gs_info; + gs_info.output_vertices = regs.vgt_gs_max_vert_out; + gs_info.num_invocations = regs.vgt_gs_instance_cnt.IsEnabled() ? regs.vgt_gs_instance_cnt.count : 1; - info.gs_info.in_primitive = regs.primitive_type; + gs_info.in_primitive = regs.primitive_type; for (u32 stream_id = 0; stream_id < Shader::GsMaxOutputStreams; ++stream_id) { - info.gs_info.out_primitive[stream_id] = + gs_info.out_primitive[stream_id] = regs.vgt_gs_out_prim_type.GetPrimitiveType(stream_id); } - info.gs_info.in_vertex_data_size = regs.vgt_esgs_ring_itemsize; - info.gs_info.out_vertex_data_size = regs.vgt_gs_vert_itemsize[0]; - - // Extract semantics offsets from a copy shader - const auto vc_stage = Shader::Stage::Vertex; - const auto* pgm_vc = regs.ProgramForStage(static_cast(vc_stage)); - const auto params_vc = Liverpool::GetParams(*pgm_vc); - DumpShader(params_vc.code, params_vc.hash, Shader::Stage::Vertex, 0, "copy.bin"); - info.gs_info.copy_data = Shader::ParseCopyShader(params_vc.code); + gs_info.in_vertex_data_size = regs.vgt_esgs_ring_itemsize; + gs_info.out_vertex_data_size = regs.vgt_gs_vert_itemsize[0]; + const auto params_vc = Liverpool::GetParams(regs.vs_program); + gs_info.vs_copy = params_vc.code; + gs_info.vs_copy_hash = params_vc.hash; + DumpShader(gs_info.vs_copy, gs_info.vs_copy_hash, Shader::Stage::Vertex, 0, "copy.bin"); break; } case Shader::Stage::Fragment: { - info.num_user_data = regs.ps_program.settings.num_user_regs; - info.num_allocated_vgprs = regs.ps_program.settings.num_vgprs * 4; + BuildCommon(regs.ps_program); const auto& ps_inputs = regs.ps_inputs; + info.fs_info.num_inputs = regs.num_interp; for (u32 i = 0; i < regs.num_interp; i++) { - info.fs_info.inputs.push_back({ + info.fs_info.inputs[i] = { .param_index = u8(ps_inputs[i].input_offset.Value()), .is_default = bool(ps_inputs[i].use_default), .is_flat = bool(ps_inputs[i].flat_shade), .default_value = u8(ps_inputs[i].default_value), - }); + }; } for (u32 i = 0; i < Shader::MaxColorBuffers; i++) { info.fs_info.color_buffers[i] = { @@ -166,9 +164,12 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_, AmdGpu::Liverpool* liverpool_) : instance{instance_}, scheduler{scheduler_}, liverpool{liverpool_}, desc_heap{instance, scheduler.GetMasterSemaphore(), DescriptorHeapSizes} { + const auto& vk12_props = instance.GetVk12Properties(); profile = Shader::Profile{ .supported_spirv = instance.ApiVersion() >= VK_API_VERSION_1_3 ? 0x00010600U : 0x00010500U, .subgroup_size = instance.SubgroupSize(), + .support_fp32_denorm_preserve = bool(vk12_props.shaderDenormPreserveFloat32), + .support_fp32_denorm_flush = bool(vk12_props.shaderDenormFlushToZeroFloat32), .support_explicit_workgroup_layout = true, }; auto [cache_result, cache] = instance.GetDevice().createPipelineCacheUnique({}); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 6088d99cf..293dfbe6a 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -459,4 +459,17 @@ void Rasterizer::ScopedMarkerInsert(const std::string_view& str) { }); } +void Rasterizer::ScopedMarkerInsertColor(const std::string_view& str, const u32 color) { + if (Config::nullGpu() || !Config::vkMarkersEnabled()) { + return; + } + + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.insertDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ + .pLabelName = str.data(), + .color = std::array( + {(f32)((color >> 16) & 0xff) / 255.0f, (f32)((color >> 8) & 0xff) / 255.0f, + (f32)(color & 0xff) / 255.0f, (f32)((color >> 24) & 0xff) / 255.0f})}); +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 82e8fc0c0..bc14f39a4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -40,6 +40,7 @@ public: void ScopeMarkerBegin(const std::string_view& str); void ScopeMarkerEnd(); void ScopedMarkerInsert(const std::string_view& str); + void ScopedMarkerInsertColor(const std::string_view& str, const u32 color); void InlineDataToGds(u32 gds_offset, u32 value); u32 ReadDataFromGds(u32 gsd_offset);